Fetch contextual data

Elevate, May 20-22-2025, Miami Beach, Florida

Deliver personalized experiences through localized content and relevant pricing.

After completing this page, you should be able to:

  • Explain how to use the localeProjection parameter in the Product Projections API to retrieve product content tailored to a specific locale.
  • Describe how to leverage Product Price Selection parameters (for example, priceCurrency, priceCountry, storeProjection, priceChannel) to retrieve accurate and context-specific pricing and inventory information using the Product Projections API.
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:

  /**
   * 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<ProductProjection> {
    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<ProductProjectionPagedQueryResponse>;

      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 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:

 /**
   * 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<ProductProjection> {
    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<ProductProjectionPagedQueryResponse>;

      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:

{
  "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.
"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.
"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.

"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.

"availability": {
  "isOnStock": true,
  "availableQuantity": 100,
  "channels": {
    "58facf50-...": {
      "isOnStock": true,
      "availableQuantity": 50
    }
  }
}

Test your knowledge