Skip to content

PODS Integration

Source: omni-web-catalog-pdp/src/services/pods/

📐 ADR: ADR-0008 — Use PODS to get product data

PODS (Product Omnichannel Data Service) is the GraphQL API for product data. It provides pricing, promotions, stock, media, characteristics, and fulfillment info. Any MFE that renders product information uses PODS.

Diagram
Terminal window
npm install @apollo/client @apollo/client-integration-nextjs graphql

Each country has a PODS store ID:

CountryStore ID
Netherlands (NL)0755
Belgium (BE)0756
France (FR)0759
Germany (DE)0751
import { registerApolloClient, ApolloClient, InMemoryCache } from '@apollo/client-integration-nextjs';
import { HttpLink, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
export function createApolloQueryFn(authenticator: Authenticator) {
const authLink = setContext(async (_, { headers }) => {
const token = await authenticator.getToken();
return { headers: { ...headers, authorization: `Bearer ${token}` } };
});
const { query } = registerApolloClient(() => {
const httpLink = new HttpLink({ uri: `${process.env.BASE_API_URL}/omni-products/v1/graphql` });
return new ApolloClient({ link: from([authLink, httpLink]), cache: new InMemoryCache() });
});
return query;
}

registerApolloClient ensures one client instance per request in RSC.

import { gql } from '@apollo/client';
export const GET_PRODUCTS = gql`
query GetProducts($skus: [String!]!, $locale: String!, $storeId: String!) {
getProducts(skus: $skus, locale: $locale, storeId: $storeId) {
identifiers { sku }
properties {
general { name description pdpUrl media { images { name url } } }
sales { price { current original } promotions { label } }
fulfillment { deliveryPromise availability }
}
}
}
`;
// Repository (data access)
export class PodsRepositoryImpl implements PodsRepository {
constructor(private readonly client: GraphQLClient) {}
async getProducts(skus: string[], apiLocale: string, storeId: string) {
return this.client.query(GET_PRODUCTS, { skus, locale: apiLocale, storeId });
}
}
// Service (business logic)
export class PodsService {
constructor(private readonly repository: PodsRepository) {}
async getProducts(skus: string[], apiLocale: ApiLocale) {
const storeId = resolveStoreId(apiLocale);
return this.repository.getProducts(skus, apiLocale, storeId);
}
}

PODS uses language_COUNTRY format:

// "nl-nl" → "nl_NL", "fr-be" → "fr_BE"
const apiLocale = locale.replace('-', '_').replace(/_.+/, (m) => m.toUpperCase());
DataDescription
IdentifiersSKU, article number
GeneralName, description, PDP URL, product type
MediaImages (from Bynder via PIM)
HierarchyCategory, headgroup, merchandise category
CharacteristicsSignings, material, dimensions, care instructions
SalesCurrent price, original price, promotions
FulfillmentDelivery promise, availability, stock
VariablePurpose
BASE_API_URLKong API base URL
AUTH_SECRETKong credentials JSON (see Kong Authentication)
  • Server-only — PODS calls happen exclusively in RSC/server actions (never client-side)
  • Always use Kong — Never call PODS directly, always through Kong with Bearer token
  • Error handling — PODS can return partial data; use a Result type pattern
  • Caching — Apollo’s InMemoryCache per request; Next.js ISR handles page-level caching