Per-operation cache hints
Added in v0.11.0. The SDK ships with no opinion about caching — by default
every operation hits the upstream API on every call. To opt into a host
framework's cache layer, attach a GraphQLFetchOptions value to the operation
and the SDK will forward it verbatim to the underlying fetch().
The type
export interface GraphQLFetchOptions {
/** Standard fetch cache mode (`'force-cache' | 'no-store' | …`). */
cache?: RequestCache;
/**
* Host-framework `fetch()` extension. The de-facto slot used by Next.js,
* Cloudflare Workers, and Nitro (when wrapping `undici`).
*/
next?: {
revalidate?: number | false;
tags?: readonly string[];
};
}
Exposed from the main entry:
import type { GraphQLFetchOptions } from '@propeller-commerce/propeller-sdk-v2';
Why the shape is narrow
GraphQLFetchOptions deliberately exposes only two fields. RequestInit
was rejected because callers could have reached method, body, headers,
and signal — those are SDK invariants (the HTTP method, the JSON payload,
the auth headers, and the 30 s timeout signal). Pick<RequestInit, 'next' | 'cache'> was rejected because next is not a standard lib.dom field —
the Pick would degenerate.
The next field name looks Next.js-coupled but is in fact the de-facto
extension slot used by every runtime that extends RequestInit — Next,
Cloudflare Workers, and Nitro (via undici). The SDK has no Next.js
dependency.
Cache-key safety
fetchOptions is never serialised into the GraphQL request body. The
wire payload remains { query, variables, operationName }. Two calls to
the same operation with different tags correctly hit the same upstream
cache entry — no cache-key pollution.
// Both of these produce IDENTICAL request bodies. A framework data cache
// (e.g. Next.js) will serve the second from cache and merge the tag sets.
client.execute({
query: productDoc,
variables: { productId: 42 },
operationName: 'product',
fetchOptions: { next: { revalidate: 300, tags: ['catalog'] } },
});
client.execute({
query: productDoc,
variables: { productId: 42 },
operationName: 'product',
fetchOptions: { next: { revalidate: 300, tags: ['product:42'] } },
});
Attaching via the service layer
Every service method takes fetchOptions as an optional trailing argument.
The KEEP / SCALAR classifier in the codegen pipeline strips this parameter
when checking alignment, so adding cache hints does not affect type drift.
import { productService } from '@propeller-commerce/propeller-sdk-v2';
const products = productService(client);
const product = await products.getProduct(
{ productId: 42, language: 'NL' },
{ next: { revalidate: 300, tags: ['catalog', 'product:42'] } },
);
Attaching via client.execute()
For direct GraphQL access (Direct GraphQL access),
fetchOptions is a field on the operation object itself:
const result = await client.execute({
query: productDoc,
variables: { productId: 42 },
operationName: 'product',
fetchOptions: {
next: { revalidate: 300, tags: ['catalog', 'product:42'] },
cache: 'force-cache',
},
});
When to use headers instead
fetchOptions is the right slot when the cache lives inside the runtime
that handles fetch() — Next.js's data cache, Cloudflare Workers' Cache
API, Nitro's storage layer.
If the cache instead lives in an application-level proxy that the SDK
calls into (e.g. a Node SSR server with its own LRU keyed by request body),
that proxy needs cache metadata in the request headers, not in
fetchOptions. For that pattern, set headers on
GraphQLClientConfig so every request
carries the metadata; the proxy reads it from the request and decides
what to do.
The propeller-vue project uses exactly this pattern — see Caching guide for both shapes side by side.
exactOptionalPropertyTypes
Under exactOptionalPropertyTypes: true, an explicit
fetchOptions: undefined is rejected. Omit the field instead:
// ❌ Type error under exactOptionalPropertyTypes
client.execute({ query, variables, operationName, fetchOptions: undefined });
// ✅ Omit when not needed
client.execute({ query, variables, operationName });
See also
- Caching guide — recipes for Next.js, Express proxy, and Cloudflare Workers / Nitro.
- Client configuration — the
headersfield for proxy-based cache transports. - Migration to 0.11.0 — the introduction note and rationale for the narrow type shape.