Storefront Next includes a type-safe API client for calling Salesforce B2C Commerce Shopper APIs, including custom APIs, from your storefront. This client provides full TypeScript support with autocomplete, type checking, and semantic operation names.
What Is SCAPI?
The B2C Commerce API (SCAPI) is a collection of RESTful APIs for interacting with B2C Commerce instances. Also known as Salesforce Commerce API or simply Commerce API, it provides endpoints for products, search, baskets, customers, orders, and so on. Create custom APIs to modify the built-in SCAPI endpoints. For example, add filters or modify responses before they get sent back to the calling application. See B2C Commerce API (SCAPI) and Custom APIs.
The SCAPI client in @salesforce/storefront-next-runtime/scapi wraps these APIs with:
Minimal bundle size: Built on openapi-fetch, a lightweight fetch client with near-zero runtime overhead.
Type-safe operations: Full TypeScript inference from OpenAPI specifications.
Semantic method names: Call getProduct() instead of GET /products/{id}.
Automatic parameter injection: Common parameters like organizationId and siteId are handled for you.
Built-in authentication helpers: Guest login, registered login, social login, passwordless login, token refresh, and password reset.
Creating API Clients
Create API clients that use createCommerceApiClients.
The clientSecret parameter determines which SLAS authentication flow the client uses.
Public client (no secret): When clientSecret is omitted, the auth helpers use the public client flow with Proof Key for Code Exchange (PKCE). This behavior is suitable for client-side apps where the secret can’t be kept secure.
Private client (with secret): When clientSecret is provided, the auth helpers use the private client flow. This behavior enables extra features, such as passwordless login, and is suitable for server-side apps where the secret can be kept secure.
Authentication
Before making API calls, you must get an access token. The client includes an auth namespace with helper functions for SLAS shopper login and session API operations.
These authentication helpers are intentionally stateless—they don’t store tokens, session data, or any intermediate state internally. Each function call is independent, giving you full control over how and where you store credentials. This design ensures compatibility with server-side rendering, avoids shared state issues across requests, and lets you choose your preferred storage strategy, such as cookies, session storage, database, and so on.
1// Step 1: Get authorization URL2const{url, codeVerifier} = await clients.auth.social.getAuthorizationUrl({3 hint: "google", // or 'facebook', 'apple', etc.4});56// Store codeVerifier securely, then redirect user to url78// Step 2: Exchange code (after user returns)9const tokens = await clients.auth.social.exchangeCode({10 code: authorizationCode,11 codeVerifier: storedCodeVerifier,12 redirectUri: "https://yoursite.com/callback",13});
Adding the Access Token to Requests
After you have an access token, add it to API requests by using middleware. Note that SLAS authentication endpoints handle their own authorization and must be skipped.
1import{SLAS_AUTH_ENDPOINTS}from "@salesforce/storefront-next-runtime/scapi";23clients.use({4 onRequest({request}){5 // Skip auth header for SLAS endpoints (they handle their own auth)6 const url = new URL(request.url);7 const isSlasAuthEndpoint = SLAS_AUTH_ENDPOINTS.some((path)=> url.pathname.includes(path));8 if(isSlasAuthEndpoint){9 return request;10}1112 request.headers.set("Authorization", `Bearer ${accessToken}`);13 return request;14},15});
Fetching Data
The client automatically includes organizationId and siteId in all requests, so you only provide operation-specific parameters.
All operations return both the parsed data and the raw response.
1const{data, response} = await clients.shopperProducts.getProduct(options);23// Typed data from the API4console.log(data.name);5console.log(data.price);67// Raw response for headers, status, etc.8console.log(response.headers.get("etag"));9console.log(response.status);
Middleware
Add custom behavior to requests and responses:
1import type{Middleware}from "@salesforce/storefront-next-runtime/scapi";23const loggingMiddleware: Middleware = {4 onRequest({request}){5 console.log(`[API] ${request.method} ${request.url}`);6 return request;7},8 onResponse({response}){9 console.log(`[API] ${response.status}`);10 return response;11},12};1314// Add to all clients15clients.use(loggingMiddleware);1617// Or add to a specific client18clients.shopperProducts.use(loggingMiddleware);
Custom APIs
Custom APIs let you extend SCAPI with your own endpoints. Storefront Next supports two ways to call a custom API from your storefront, and both reuse the same authentication and middleware patterns as the built-in shopper clients.
Generate a Typed Client (Recommended)
Generate a fully typed client from your custom API’s OpenAPI schema. You get autocomplete, parameter validation, and operation-name calls like loyaltyClient.getLoyaltyInfo() instead of hand-rolled paths.
Storefront Next’s template app ships a sfnext scapi CLI that takes an OpenAPI 3 schema and generates the type definitions and operation map for you, then registers the client alongside the built-in shopper clients. See the template’s SCAPI guide for the project-level workflow.
An external project follows the same pattern with three components:
Types and operation map generated from the OpenAPI schema. The types are produced with openapi-typescript; the operation map records each operation’s HTTP method and path so the client can dispatch by operationId.
A typed client built with createOpenApiFetchClient and wrapped with the runtime’s createClient helper to expose operation-name methods.
Declare the operation map as const so its m / b / s values stay literal — the proxy uses those literals to type each operation method’s parameters and return value.
1// ./generated/loyalty-info.operations.ts (generated alongside the types)2export const BASE_PATH = "/organizations/{organizationId}" as const;3export const operations = {4 getLoyaltyInfo:{m: "GET" as const, b: BASE_PATH, s: "/customers"},5}as const;
1// loyalty-client.ts2import{3 createOpenApiFetchClient,4 createClient,5}from "@salesforce/storefront-next-runtime/scapi";6import type{paths}from "./generated/loyalty-info";7import{operations}from "./generated/loyalty-info.operations";89const base = createOpenApiFetchClient<paths>({10 // Stop before /organizations — createClient appends the operation map's11 // base path, which already includes /organizations/{organizationId}.12 baseUrl: "https://shortcode.api.commercecloud.salesforce.com/custom/loyalty-info/v1",13});1415const loyaltyClient = createClient(base, operations, {16 organizationId: "f_ecom_xxx",17 siteId: "RefArch",18});1920// Register an auth middleware the same way the built-in clients do — see21// [Adding the Access Token to Requests](#adding-the-access-token-to-requests).22loyaltyClient.use(authMiddleware);2324const{data} = await loyaltyClient.getLoyaltyInfo({25 params:{query:{c_customer_id: "customer1"}},26});27// GET https://shortcode.api.commercecloud.salesforce.com/custom/loyalty-info/v128// /organizations/f_ecom_xxx/customers?siteId=RefArch&c_customer_id=customer1
Call with fetch Directly
For one-off calls, or projects without a codegen step, call the endpoint directly with fetch. Pull accessToken, organizationId, siteId, and any other dynamic values from the same auth/session context the typed clients use, and let the URL API encode user-supplied query values for you.
1const url = new URL(2 `https://shortcode.api.commercecloud.salesforce.com/custom/loyalty-info/v1/organizations/${organizationId}/customers`,3);4url.searchParams.set("c_customer_id", customerId);5url.searchParams.set("siteId", siteId);67const response = await fetch(url, {8 headers:{Authorization: `Bearer ${accessToken}`},9});10const data = await response.json();
The native-fetch path skips the typed-client features above (TypeScript types, automatic organizationId/siteId injection, ApiError wrapping) — prefer the generated client for any custom API you call from more than one place.