Skip to content

New: AI agent integration via Model Context Protocol (MCP).Learn more

Multi-store support

Evolve supports multiple stores, locales, and currencies from a single backend deployment. Every request carries a StoreContext that tells each service which store, language, and currency to use. This enables scenarios like running separate country stores, localized storefronts, or B2B stores with different assortments from the same infrastructure.

The StoreContext has three properties:

  • storeKey: identifies the store
  • locale: the language for the request (e.g. nl-NL, en-GB)
  • currency: the currency for prices and carts (e.g. EUR, GBP)

These three values flow through the entire request lifecycle, from the frontend through the gateway to every domain service. All services receive the same StoreContext, regardless of which SAAS backend they integrate with.

sequenceDiagram
    participant Frontend
    participant Gateway as GraphQL Gateway
    participant Commerce as Commerce Service
    participant CMS as CMS Service

    Frontend->>Gateway: GraphQL request + headers<br/>X-StoreContext-StoreKey<br/>X-StoreContext-Locale<br/>X-StoreContext-Currency
    Gateway->>Gateway: Read headers into StoreContext
    Gateway->>Commerce: Federated subquery (headers forwarded)
    Gateway->>CMS: Federated subquery (headers forwarded)
    Commerce->>Commerce: readStoreContextFromRequest()
    CMS->>CMS: readStoreContextFromRequest()
    Commerce-->>Gateway: Store-scoped, localized data
    CMS-->>Gateway: Localized content
    Gateway-->>Frontend: Merged response

The frontend sets the three X-StoreContext-* headers on every GraphQL request, both server-side and client-side:

const fetcher = initClientFetcher("/graphql", {
defaultHeaders: {
"X-StoreContext-StoreKey": storeConfig.storeKey,
"X-StoreContext-Locale": locale,
"X-StoreContext-Currency": storeConfig.currency,
},
});

The store key and currency come from the store configuration (selected per deployment via the STORE_KEY environment variable). The locale comes from the URL path via next-intl routing (e.g. /nl-NL/products/...).

The GraphQL gateway (Hive Router) forwards the headers from the incoming request to every subgraph service. This is configured declaratively in router.config.yaml:

headers:
all:
request:
- propagate:
named:
- X-StoreContext-StoreKey
- X-StoreContext-Locale
- X-StoreContext-Currency

Each service reads the headers from the incoming request and constructs a StoreContext object that resolvers can access via context.storeContext:

import { readStoreContextFromRequest } from "@evolve-framework/core/service/graphql";
const storeContext = readStoreContextFromRequest(request);

All three headers are required. If any is missing, the request fails with a GraphQL error.

Every service uses StoreContext, but what each property means depends on the integration.

Commerce services use all three properties to scope operations to the correct store:

  • storeKey: scopes API calls to a specific store. Carts, orders, customers, and shopping lists are all queried within the store context.
  • locale: determines which translations are returned for product names, descriptions, and category names.
  • currency: selects the correct prices. Together with the country (derived from the locale), it drives price selection, tax calculation, and cart operations.
  • Product projections: the store context provides parameters for product queries that filter results by store assortment, locale, and pricing.

CMS services (Contentful, Storyblok) use StoreContext primarily for content localization:

  • locale: determines which language version of content to fetch from the CMS. Contentful uses it to filter entries by locale, Storyblok extracts the language code for its API.
  • storeKey: used in cache keys to keep content separated per store when different stores serve different content.
  • currency: included in cache keys to ensure different currency contexts don’t share cached data.

All services use StoreContext to generate cache key prefixes. This ensures that cached data from one store, locale, or currency does not leak into another:

public get cacheKeyPrefix(): string {
return `${this.storeKey}-${this.locale}-${this.currency}`;
}

Each frontend deployment is configured for a specific store through the STORE_KEY environment variable. This selects a store configuration that defines:

  • Store key: identifies the store across all backend services
  • Default locale and available locales: which languages the store supports
  • Currency: the store’s currency
  • Country: used for tax and shipping calculations
  • Available payment methods: which payment providers are enabled
  • Checkout configuration: checkout-specific settings
  • Distribution channel: for store-specific product assortment and pricing

To run multiple stores, deploy multiple frontend instances with different STORE_KEY values. They all share the same backend.

In B2B scenarios, a customer may belong to a business unit that is assigned to a specific store. When this is the case, the ClientContext overrides the store key from the request with the business unit’s store key:

get storeKey(): string {
if (!this.isAuthenticated()) {
return this.storeContext.storeKey;
}
return this._info?.businessContext?.storeKey
?? this.storeContext.storeKey;
}

This allows B2B customers to see store-specific assortments and pricing based on their business unit, even when accessing the platform through a shared storefront. See B2B & B2C architecture for more details on business units and the BusinessContext.