Interface: ServerClientOptions
Extends
Omit<StorefrontClientConfig,"middleware">
Properties
apiUrl
apiUrl:
string
GraphQL API URL (e.g. 'https://api.doswiftly.pl')
Inherited from
buildApiUrl?
optionalbuildApiUrl?:string|null
Host to read storefront data from WHILE A BUILD IS RUNNING, instead of apiUrl.
A shop served from its own api.{apex} sits behind that zone's bot protection, which
challenges precisely what a build looks like — a datacenter address issuing one burst —
and cannot be waived for it. Reading from the platform host during the build sidesteps
that, while the browser bundle keeps pointing at the merchant host, which is the whole
reason that host exists.
Applies ONLY during a build; live server rendering always uses apiUrl. Defaults to
DOSWIFTLY_BUILD_API_URL, which the deploy pipeline sets. Pass a value to pin it, or
null to keep using apiUrl even during a build.
debug?
optionaldebug?:boolean|"verbose"|DebugOptions
Enable debug logging (request/response in dev).
Tagged union (backward-compatible):
false/ omitted — no logging.true— minimal logs: operationName + variables on request, status + hasErrors + userErrors on response. The exact output is preserved from the v17.1.0 minimal mode.'verbose'— every dimension on: full query + variables + headers + response body + userErrors + timing.DebugOptions— granular opt-in per dimension.
Falls back to process.env.DOSWIFTLY_SDK_DEBUG ('verbose' / 'true' / '1') when this prop is
omitted. NODE_ENV=production requires an explicit debug setting — env-driven verbose mode is
no-op in production to prevent accidental logging of customer PII.
Authorization: Bearer … and customerAccessToken=… cookie values are unconditionally redacted
to ***<last4> whenever headers are logged, regardless of mode.
Inherited from
defaultHeaders?
optionaldefaultHeaders?:Record<string,string>
Default headers for all requests
Inherited from
StorefrontClientConfig.defaultHeaders
fetch?
optionalfetch?: {(input,init?):Promise<Response>; (input,init?):Promise<Response>; }
Custom fetch implementation (polyfill, test mocks, edge)
Call Signature
(
input,init?):Promise<Response>
Parameters
input
RequestInfo | URL
init?
RequestInit
Returns
Promise<Response>
Call Signature
(
input,init?):Promise<Response>
Parameters
input
string | Request | URL
init?
RequestInit
Returns
Promise<Response>
Inherited from
getBuyerIp?
optionalgetBuyerIp?: () =>string|Promise<string|null|undefined> |null|undefined
Buyer-IP source that ENABLES forwarded-IP signing (opt-in). The forwarded-IP middleware is wired ONLY when this is provided — without it the client never reads the buyer IP and never signs (a fully static-safe, inert pass-through).
Reading the buyer IP needs a request-scoped dynamic API (e.g. next/headers
headers()), which is ILLEGAL in statically-generated / ISR routes — calling it
there crashes the page ("static to dynamic at runtime"). So provide this ONLY on
routes that are already dynamic (per-request rendered). A server-rendered
storefront otherwise collapses every buyer onto its own server IP for rate
limiting; forwarding the real IP restores per-buyer limits. May be async.
Server-side only.
Returns
string | Promise<string | null | undefined> | null | undefined
Example
// ONLY on a dynamic route — `headers()` forces dynamic rendering:
import { headers } from 'next/headers';
getStorefrontClient({ apiUrl, shopSlug, getBuyerIp: async () => (await headers()).get('cf-connecting-ip') });
getForwardedIpSecret?
optionalgetForwardedIpSecret?: () =>string|Promise<string|null|undefined> |null|undefined
OPTIONAL override for the forwarded-IP signing secret. By default it is read
from process.env.DOSWIFTLY_FORWARDED_IP_SECRET, set in your DoSwiftly
deployment environment. Provide this getter only to override the env source —
e.g. a runtime that does not expose the secret on process.env. Lazy getter —
a rotated secret is picked up without rebuilding the client. NEVER expose this
to the browser. Sync or async.
Returns
string | Promise<string | null | undefined> | null | undefined
graphqlPath?
optionalgraphqlPath?:string
GraphQL endpoint path appended to apiUrl. Defaults to /storefront/graphql
(the public catalog surface — cacheable). Set to /storefront/customer/graphql
to target the customer-account surface (orders, addresses, wishlist, loyalty),
which is always served fresh (never shared-cached). Must start with /.
Inherited from
StorefrontClientConfig.graphqlPath
maxConcurrency?
optionalmaxConcurrency?:number
Maximum data requests this process keeps in flight at once. 0 disables the cap.
Unbounded fan-out from one process is what rate limits punish: a production build renders every page in one process and fires its requests as fast as the renderer schedules them, and a sitemap route or a bulk migration script does the same. Capping spreads the same work over a window the API will serve. The budget is shared by every client in the process, so a per-request client still counts against it.
When you pass a value it is always honoured. When you do not, the deploy pipeline's build
environment supplies one (DOSWIFTLY_BUILD_CONCURRENCY, default 6) and live rendering is
left uncapped, so no visitor request ever queues behind another.
middleware?
optionalmiddleware?:Middleware[]
Additional middleware (prepended to default pipeline).
Use this for request-scoped headers (e.g. currency from cookie, language from URL, custom tracking):
getStorefrontClient({
apiUrl, shopSlug,
middleware: [
async (req, next) => {
req.headers['X-Preferred-Currency'] = await readCookieFromHeaders();
return next(req);
},
],
});
minRequestIntervalMs?
optionalminRequestIntervalMs?:number
Minimum spacing between request starts, in milliseconds. 0 disables it.
A ceiling on requests in flight does not bound requests per MINUTE, which is what a server-side rate limit measures — six slots against fast responses still sustains tens of requests per second. Spacing is what bounds it.
Same resolution as maxConcurrency: an explicit value always wins, otherwise a deploy
build supplies one (DOSWIFTLY_BUILD_MIN_INTERVAL_MS, default 200 ms ≈ 300 requests per
minute) and live rendering is left unpaced.
shopSlug
shopSlug:
string
Shop slug for multi-tenant routing
Inherited from
StorefrontClientConfig.shopSlug
telemetry?
optionaltelemetry?:boolean| {endpoint?:string;heartbeatIntervalMs?:number;includeNonProduction?:boolean; }
Cookieless visitor telemetry (page-load ping + heartbeat while the tab is visible). Collects the raw referrer, user agent and an ephemeral per-tab session id — no cookies, no PII.
Enabled by default on a live shop; pass false to opt out. Nothing is sent from a local
address (localhost, a loopback IP, a .local / .localhost name) or from a build whose
mode is not production, so your development sessions stay out of the shop's figures.
Set includeNonProduction: true when you deliberately want to measure such an environment.
Inherited from
StorefrontClientConfig.telemetry
trustedDocuments?
optionaltrustedDocuments?:boolean
Whether the target GraphQL surface resolves persisted-document ids (trusted documents). The
default public catalog endpoint does; the customer-account endpoint does not. When false, the
client always sends the full query — it never uses the cacheable GET nor sends a documentId
over POST. Defaults to true. createCustomerClient forces false; set it yourself only when
pointing createStorefrontClient at a custom endpoint without persisted-document support.