From 3bd96bac5aaea2baf022149f1e814cfc6c2be4c8 Mon Sep 17 00:00:00 2001 From: Tharaa Elmorssi Date: Wed, 31 Dec 2025 12:08:34 +1100 Subject: [PATCH 1/2] feat: Remove caching on fetching product inventory data --- .changeset/eager-nails-bake.md | 10 ++++ .../(default)/product/[slug]/page-data.ts | 46 +++++++++++++++---- .../(default)/product/[slug]/page.tsx | 25 ++++++++-- 3 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 .changeset/eager-nails-bake.md diff --git a/.changeset/eager-nails-bake.md b/.changeset/eager-nails-bake.md new file mode 100644 index 0000000000..cce2f53695 --- /dev/null +++ b/.changeset/eager-nails-bake.md @@ -0,0 +1,10 @@ +--- +"@bigcommerce/catalyst-core": minor +--- + +Fetch product inventory data with a separate GQL query with no caching + +## Migration +The files to be rebased for this change to be applied are: +- core/app/[locale]/(default)/product/[slug]/page-data.ts +- core/app/[locale]/(default)/product/[slug]/page.tsx \ No newline at end of file diff --git a/core/app/[locale]/(default)/product/[slug]/page-data.ts b/core/app/[locale]/(default)/product/[slug]/page-data.ts index 0f7fccc2c4..82e7e396d2 100644 --- a/core/app/[locale]/(default)/product/[slug]/page-data.ts +++ b/core/app/[locale]/(default)/product/[slug]/page-data.ts @@ -254,7 +254,7 @@ export const getStreamableProductVariant = cache( document: StreamableProductVariantBySkuQuery, variables, customerAccessToken, - fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } }, + fetchOptions: { cache: 'no-store' }, }); return data.site.product?.variants; @@ -310,6 +310,36 @@ const StreamableProductQuery = graphql( minPurchaseQuantity maxPurchaseQuantity warranty + ...ProductViewedFragment + ...ProductSchemaFragment + } + } + } + `, + [ProductViewedFragment, ProductSchemaFragment], +); + +type Variables = VariablesOf; + +export const getStreamableProduct = cache( + async (variables: Variables, customerAccessToken?: string) => { + const { data } = await client.fetch({ + document: StreamableProductQuery, + variables, + customerAccessToken, + fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } }, + }); + + return data.site.product; + }, +); + +const StreamableProductInventoryQuery = graphql( + ` + query StreamableProductInventoryQuery($entityId: Int!) { + site { + product(entityId: $entityId) { + sku inventory { hasVariantInventory isInStock @@ -324,25 +354,23 @@ const StreamableProductQuery = graphql( availabilityV2 { status } - ...ProductViewedFragment ...ProductVariantsInventoryFragment - ...ProductSchemaFragment } } } `, - [ProductViewedFragment, ProductSchemaFragment, ProductVariantsInventoryFragment], + [ProductVariantsInventoryFragment], ); -type Variables = VariablesOf; +type ProductInventoryVariables = VariablesOf; -export const getStreamableProduct = cache( - async (variables: Variables, customerAccessToken?: string) => { +export const getStreamableProductInventory = cache( + async (variables: ProductInventoryVariables, customerAccessToken?: string) => { const { data } = await client.fetch({ - document: StreamableProductQuery, + document: StreamableProductInventoryQuery, variables, customerAccessToken, - fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } }, + fetchOptions: { cache: 'no-store' }, }); return data.site.product; diff --git a/core/app/[locale]/(default)/product/[slug]/page.tsx b/core/app/[locale]/(default)/product/[slug]/page.tsx index b432679047..10469c1e52 100644 --- a/core/app/[locale]/(default)/product/[slug]/page.tsx +++ b/core/app/[locale]/(default)/product/[slug]/page.tsx @@ -28,6 +28,7 @@ import { getProductPricingAndRelatedProducts, getStreamableInventorySettingsQuery, getStreamableProduct, + getStreamableProductInventory, getStreamableProductVariant, } from './page-data'; @@ -118,8 +119,22 @@ export default async function Product({ params, searchParams }: Props) { const streamableProductSku = Streamable.from(async () => (await streamableProduct).sku); + const streamableProductInventory = Streamable.from(async () => { + const variables = { + entityId: Number(productId), + }; + + const product = await getStreamableProductInventory(variables, customerAccessToken); + + if (!product) { + return notFound(); + } + + return product; + }); + const streamableProductVariant = Streamable.from(async () => { - const product = await streamableProduct; + const product = await streamableProductInventory; if (!product.inventory.hasVariantInventory) { return undefined; @@ -192,7 +207,7 @@ export default async function Product({ params, searchParams }: Props) { }); const streameableCtaLabel = Streamable.from(async () => { - const product = await streamableProduct; + const product = await streamableProductInventory; if (product.availabilityV2.status === 'Unavailable') { return t('ProductDetails.Submit.unavailable'); @@ -210,7 +225,7 @@ export default async function Product({ params, searchParams }: Props) { }); const streameableCtaDisabled = Streamable.from(async () => { - const product = await streamableProduct; + const product = await streamableProductInventory; if (product.availabilityV2.status === 'Unavailable') { return true; @@ -257,7 +272,7 @@ export default async function Product({ params, searchParams }: Props) { const streamableStockDisplayData = Streamable.from(async () => { const [product, variant, inventorySetting] = await Streamable.all([ - streamableProduct, + streamableProductInventory, streamableProductVariant, streamableInventorySettings, ]); @@ -347,7 +362,7 @@ export default async function Product({ params, searchParams }: Props) { const streamableBackorderDisplayData = Streamable.from(async () => { const [product, variant, inventorySetting] = await Streamable.all([ - streamableProduct, + streamableProductInventory, streamableProductVariant, streamableInventorySettings, ]); From d424c679f8ac61d26fae6013d2c390398f19a0f6 Mon Sep 17 00:00:00 2001 From: Tharaa Elmorssi Date: Thu, 5 Feb 2026 19:38:44 +1100 Subject: [PATCH 2/2] feat: Remove caching on fetching product inventory data - Apply comments --- .../[locale]/(default)/product/[slug]/page-data.ts | 14 +++++++------- .../app/[locale]/(default)/product/[slug]/page.tsx | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/app/[locale]/(default)/product/[slug]/page-data.ts b/core/app/[locale]/(default)/product/[slug]/page-data.ts index 82e7e396d2..fcdfc0fab9 100644 --- a/core/app/[locale]/(default)/product/[slug]/page-data.ts +++ b/core/app/[locale]/(default)/product/[slug]/page-data.ts @@ -210,7 +210,7 @@ export const getProduct = cache(async (entityId: number, customerAccessToken?: s return data.site; }); -const StreamableProductVariantBySkuQuery = graphql(` +const StreamableProductVariantInventoryBySkuQuery = graphql(` query ProductVariantBySkuQuery($productId: Int!, $sku: String!) { site { product(entityId: $productId) { @@ -246,15 +246,15 @@ const StreamableProductVariantBySkuQuery = graphql(` } `); -type VariantVariables = VariablesOf; +type VariantInventoryVariables = VariablesOf; -export const getStreamableProductVariant = cache( - async (variables: VariantVariables, customerAccessToken?: string) => { +export const getStreamableProductVariantInventory = cache( + async (variables: VariantInventoryVariables, customerAccessToken?: string) => { const { data } = await client.fetch({ - document: StreamableProductVariantBySkuQuery, + document: StreamableProductVariantInventoryBySkuQuery, variables, customerAccessToken, - fetchOptions: { cache: 'no-store' }, + fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate: 60 } }, }); return data.site.product?.variants; @@ -370,7 +370,7 @@ export const getStreamableProductInventory = cache( document: StreamableProductInventoryQuery, variables, customerAccessToken, - fetchOptions: { cache: 'no-store' }, + fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate: 60 } }, }); return data.site.product; diff --git a/core/app/[locale]/(default)/product/[slug]/page.tsx b/core/app/[locale]/(default)/product/[slug]/page.tsx index 10469c1e52..c07049181a 100644 --- a/core/app/[locale]/(default)/product/[slug]/page.tsx +++ b/core/app/[locale]/(default)/product/[slug]/page.tsx @@ -29,7 +29,7 @@ import { getStreamableInventorySettingsQuery, getStreamableProduct, getStreamableProductInventory, - getStreamableProductVariant, + getStreamableProductVariantInventory, } from './page-data'; interface Props { @@ -133,7 +133,7 @@ export default async function Product({ params, searchParams }: Props) { return product; }); - const streamableProductVariant = Streamable.from(async () => { + const streamableProductVariantInventory = Streamable.from(async () => { const product = await streamableProductInventory; if (!product.inventory.hasVariantInventory) { @@ -145,7 +145,7 @@ export default async function Product({ params, searchParams }: Props) { sku: product.sku, }; - const variants = await getStreamableProductVariant(variables, customerAccessToken); + const variants = await getStreamableProductVariantInventory(variables, customerAccessToken); if (!variants) { return undefined; @@ -273,7 +273,7 @@ export default async function Product({ params, searchParams }: Props) { const streamableStockDisplayData = Streamable.from(async () => { const [product, variant, inventorySetting] = await Streamable.all([ streamableProductInventory, - streamableProductVariant, + streamableProductVariantInventory, streamableInventorySettings, ]); @@ -363,7 +363,7 @@ export default async function Product({ params, searchParams }: Props) { const streamableBackorderDisplayData = Streamable.from(async () => { const [product, variant, inventorySetting] = await Streamable.all([ streamableProductInventory, - streamableProductVariant, + streamableProductVariantInventory, streamableInventorySettings, ]);