# Fetch contextual data Delivering a personalized experience on your PDP is crucial for customer satisfaction. This involves showing customers content in their preferred language and tone of voice. By using the `storeProjection` in the Product Projection API, you can retrieve tailored data that enhances the customer's journey. This page will guide you through the key aspects of contextual data, focusing on locale awareness to present content in the customer's language and price and inventory selection to display relevant pricing and availability. Understanding and implementing these techniques can lead to increased customer engagement and a more seamless shopping experience. ## Locale awareness Customers expect to see product information in their preferred language. The `localeProjection` query parameter allows you to retrieve one language with the matching localized fields. By explicitly setting `localeProjection` with a locale code (for example, `en-US`, `de-DE`), you instruct the API to return only the `name`, `description`, `slug`, and other localized fields for that specific language. ### Code example Below is a sample code snippet using the TypeScript SDK that demonstrates how to fetch localized product data. In this example, the `localeProjection` parameter is explicitly set to ensure that the returned product fields are in the correct language: ```typescript /** * Fetches a localized product projection using slug and localeProjection. * @param slug - The slug to search for. * @param locale - The locale for localization. * @returns The localized product projection. */ async fetchLocalizedProductBySlug( slug: string, locale: string, ): Promise { try { const response = (await apiRoot .productProjections() .get({ queryArgs: { where: `slug(${locale} = "${slug}")`, staged: false, localeProjection: locale, // Only return fields in the given locale withTotal: false }, }) .execute()) as ClientResponse; const product = response.body.results?.[0]; if (!product) { throw new NotFoundException( `Localized product with slug '${slug}' not found.`, ); } return product; } catch (error) { this.handleServiceError(error, `localized slug '${slug}'`); } } ``` ### Key takeaways - `localeProjection: locale`: filters localized fields to return only those matching the specified locale (for example, `en-US`). - `where: slug(${locale} = "...")`: ensures you are querying the correct localized slug. - **Benefit:** provides precise localization, improving clarity and trust for your customers. ## Accurate price and inventory selection Displaying accurate pricing and availability based on a customer's context (region, store, group, channel) is essential. The Product Projection API supports this through its [Product Price Selection](/api/pricing-and-discounts-overview.md#product-price-selection) parameters, such as `priceCurrency`, `priceCountry`, `storeProjection`, and `priceChannel`. Using these parameters ensures the API response identifies the single, best-matching price for that specific context. If you omit these price selection arguments, the API will return an array containing all available prices for the Product filtered by the Store. Your application would then need to implement its own logic to determine which price applies to the customer based on the Product Price Selection rules. While building this logic yourself is possible, leveraging the API's built-in price selection parameters is significantly simpler, less error-prone, and the recommended approach. - `priceCurrency`: specifies the currency in which the price should be displayed (for example, `"AUD"`). - `priceCountry`: filters prices based on the country (for example, `"AU"`), ensuring regional pricing compliance. - `storeProjection`: ensures returned data (prices, inventory) matches the configuration of the specified store. This allows for store-specific visibility and pricing. This will also return any Tailored Product data. - `priceChannel`: filters product prices based on the provided price channel. ### Code example Below is a sample code snippet that demonstrates how to fetch a product with pricing and inventory tailored to the customer's context in your Backend-for-Frontend (BFF) layer: ```typescript /** * Fetches a product projection with price and inventory information. * @param slug - Product slug. * @param locale - Customer locale. * @param priceCurrency - Currency code (e.g., "AUD"). * @param priceCountry - Country code (e.g., "AU"). * @param storeKey - Store key. * @param priceChannelId - Channel ID for pricing. * @returns The enriched product projection. */ async fetchProductWithPriceAndInventory( slug: string, locale: string, priceCurrency: string, priceCountry: string, storeKey: string, priceChannelId: string, ): Promise { try { const response = (await apiRoot .productProjections() .get({ queryArgs: { where: `slug(${locale} = "${slug}")`, staged: false, withTotal: false, localeProjection: locale, priceCurrency, priceCountry, storeProjection: storeKey, priceChannel: priceChannelId, }, }) .execute()) as ClientResponse; const product = response.body.results?.[0]; if (!product) { throw new NotFoundException(`Product with slug '${slug}' not found.`); } this.logger.log( `Fetched product with pricing: ${JSON.stringify(product)}`, ); return product; } catch (error) { this.handleServiceError(error, `slug '${slug}' with price & inventory`); } } ``` ### Key takeaways - Using Product Price Selection parameters will return a new field `price` with the single best matching value. Note that the `prices` field is also still returned, but will be filtered by the Store distribution channels. ### Execute and observe the response Call the `fetchProductWithPriceAndInventory()`, and inspect the JSON response. When executed correctly, the response will include important context-aware fields based on your query parameters. Here's a sample response: ```json { "id": "2bbb247c-9d08-4018-b3ee-07500af7b50b", "version": 15, "name": { "en-US": "Charlie Armchair" }, "description": { "en-US": "A corduroy chair with wooden legs has a cozy and rustic feel..." }, "slug": { "en-US": "charlie-armchair" }, "masterVariant": { "sku": "CARM-023", "prices": [ { "id": "aa950e94-f3dd-4855-b621-4db87110c96f", "value": { "currencyCode": "USD", "centAmount": 49900 }, "country": "US", "discounted": { "value": { "currencyCode": "USD", "centAmount": 42415 } } }, { "id": "bb0d9bd1-09a5-4269-97a2-5d090fe85829", "value": { "currencyCode": "USD", "centAmount": 8900 }, "country": "US", "channel": { "typeId": "channel", "id": "ba248022-a551-4923-ad18-e310ea215ac6" }, "discounted": { "value": { "currencyCode": "USD", "centAmount": 7565 } } } ], "price": { "id": "d9e53b55-b46f-4ed0-9946-b2de11736504", "value": { "currencyCode": "USD", "centAmount": 30000 }, "discounted": { "value": { "currencyCode": "USD", "centAmount": 25500 } }, "channel": { "typeId": "channel", "id": "ba248022-a551-4923-ad18-e310ea215ac6" } }, "availability": { "isOnStock": true, "availableQuantity": 100, "channels": { "58facf50-1a75-480b-b6ad-cea4df710a82": { "isOnStock": true, "availableQuantity": 50 } } } } } ``` When calling `fetchProductWithPriceAndInventory`, the Product Projection API dynamically adjusts the returned Product data based on your provided context—locale, currency, country, store, and price channel. Here’s how to interpret key fields in the response: - **Localized fields** (`name`, `description`, `slug`, `attributes`): - Only fields for the specified locale (for example, `"en-US"`) are included in the response because `localeProjection` is explicitly passed. You can use Locales or `storeProjection` to filter the locale response. Remember that a Store can have multiple locales, whilst the `locales` parameter is always one. ```json "name": { "en-US": "Charlie Armchair" } ``` - **Why this matters**: this ensures your frontend UI displays only the customer’s preferred language and avoids unintentional fallbacks or language switching logic on the frontend. - **Prices array** (`masterVariant.prices`): - The `prices` array contains only prices relevant to the provided context `storeProjection`. - This is a **filtered subset** of all available prices. For example, while the product may have prices for `EUR`, `GBP`, and `USD` in the backend, this response includes only `USD` prices applicable to the U.S. market and to a specific Store/Channel. ```json "prices": [ { "id": "aa950e94-f3dd-4855-b621-4db87110c96f", "value": { "currencyCode": "USD", "centAmount": 49900 }, "country": "US", "discounted": { "value": { "currencyCode": "USD", "centAmount": 42415 } } }, { "id": "bb0d9bd1-09a5-4269-97a2-5d090fe85829", "value": { "currencyCode": "USD", "centAmount": 8900 }, "country": "US", "channel": { "typeId": "channel", "id": "ba248022-a551-4923-ad18-e310ea215ac6" }, "discounted": { "value": { "currencyCode": "USD", "centAmount": 7565 } } } ] ``` - **Selected** `price` **field** - The `price` field reflects the best-matching price automatically chosen by Composable Commerce. - This is calculated based on price selection precedence, considering currency, country, Store, and Channel. - If a discounted price is applicable (based on rules and active product discounts), it appears inside `discounted`. ```json "price": { "id": "d9e53b55-b46f-4ed0-9946-b2de11736504", "value": { "currencyCode": "USD", "centAmount": 30000 }, "discounted": { "value": { "currencyCode": "USD", "centAmount": 25500 } }, "channel": { "typeId": "channel", "id": "ba248022-a551-4923-ad18-e310ea215ac6" } } ``` - **Inventory / availability** (`availability`) - The response shows stock status for both global and Store-specific inventory (per `storeProjection`). - Inside `channels`, you’ll find inventory details specific to your store’s supply channel. ```json "availability": { "isOnStock": true, "availableQuantity": 100, "channels": { "58facf50-...": { "isOnStock": true, "availableQuantity": 50 } } } ``` ## Related pages - [Area overview page with navigation](/learning-implement-product-discovery-and-presentation.md) - [Previous page: Fetch Product data](/learning-implement-product-discovery-and-presentation/build-product-detail-pages/fetch-product-data.md) - [Next page: Use variant switching and media](/learning-implement-product-discovery-and-presentation/build-product-detail-pages/use-variant-switching-and-media.md)