@thor-commerce/graphql-client
v1.0.6
Published
Fetch-based GraphQL client for Thor Commerce APIs
Downloads
2,188
Readme
@thor-commerce/graphql-client
Thin but production-ready GraphQL client for Thor Commerce Admin and Storefront APIs.
It is intentionally transport-focused:
- builds Thor API URLs
- applies auth and default headers
- handles retries for transient HTTP failures
- returns structured GraphQL errors
- supports
@deferstream responses
If you want Apollo, urql, or another cache/client layer, use this package underneath or skip it entirely and bring your own client.
Installation
npm install @thor-commerce/graphql-clientQuick Start
import {createAdminGraphQLClient} from "@thor-commerce/graphql-client";
const admin = createAdminGraphQLClient({
tenant: "demo",
apiKey: process.env.THOR_ADMIN_API_KEY!,
});
const response = await admin.request<{
stores: {nodes: Array<{id: string; name: string}>};
}>({
query: `
query Stores {
stores {
nodes {
id
name
}
}
}
`,
});
if (response.errors) {
console.error(response.errors);
} else {
console.log(response.data?.stores.nodes);
}Client Constructors
Use the generic constructor when you want one shared API:
import {ApiType, createGraphQLClient} from "@thor-commerce/graphql-client";
const client = createGraphQLClient({
apiType: ApiType.Storefront,
tenant: "demo",
apiKey: process.env.THOR_STOREFRONT_API_KEY!,
});Use the convenience constructors for cleaner call sites:
import {
createAdminGraphQLClient,
createStorefrontGraphQLClient,
} from "@thor-commerce/graphql-client";Request Modes
request() returns a structured result and does not throw for normal API failures:
const response = await admin.request<MyQuery, MyVariables>(query, {
variables: {id: "gid://thor/Product/1"},
});
if (response.errors) {
console.error(response.errors.networkStatusCode);
console.error(response.errors.graphQLErrors);
}requestOrThrow() is the ergonomic path when you want application-style exceptions:
const data = await admin.requestOrThrow<MyQuery>(query);Typed Operations With @thor-commerce/graphql-codegen-preset
If you want generated result and variable types, pair this package with @thor-commerce/graphql-codegen-preset.
The cleanest setup today is to use client: ClientType.Apollo in codegen so you get operation result and variable types without coupling to another runtime client.
import {
ApiType,
ClientType,
thorCommerceApiProject,
} from "@thor-commerce/graphql-codegen-preset";
export default {
projects: {
storefront: thorCommerceApiProject({
apiType: ApiType.Storefront,
client: ClientType.Apollo,
outputDir: "./src/graphql",
documents: ["./src/**/*.{ts,tsx}"],
}),
},
};After running codegen, import the generated operation types and use them as generics on the Thor client:
import {createStorefrontGraphQLClient} from "@thor-commerce/graphql-client";
import type {
GetProductQuery,
GetProductQueryVariables,
} from "./graphql/storefront.generated";
const client = createStorefrontGraphQLClient({
tenant: "demo",
apiKey: process.env.THOR_STOREFRONT_API_KEY!,
});
const GET_PRODUCT_QUERY = `#graphql
query GetProduct($handle: String!) {
product(handle: $handle) {
id
name
}
}
`;
const data = await client.requestOrThrow<
GetProductQuery,
GetProductQueryVariables
>(GET_PRODUCT_QUERY, {
variables: {
handle: "classic-tee",
},
});That gives you:
- typed variables at the call site
- typed response data from
request()orrequestOrThrow() - the Thor runtime client for transport, retries, and error handling
requestStream() is for operations that declare @defer:
const stream = await admin.requestStream<ProductsQuery>(`
query Products {
products @defer {
nodes {
id
name
}
}
}
`);
for await (const chunk of stream) {
if (chunk.errors) {
console.error(chunk.errors);
break;
}
console.log(chunk.data, chunk.hasNext);
}Options
const client = createAdminGraphQLClient({
tenant: "demo",
apiKey: process.env.THOR_ADMIN_API_KEY!,
headers: {
"X-Trace-Id": "trace-123",
},
retries: 2,
retriableStatusCodes: [429, 500, 502, 503, 504],
defaultRetryWaitTime: 1_000,
deprecationHeader: "X-Thor-Api-Deprecated-Reason",
logger(event) {
console.log(event.type, event.content);
},
});Supported options:
tenantapiKeyapiTypebaseUrlheadersfetchretriesretriableStatusCodesdefaultRetryWaitTimedeprecationHeaderlogger
Notes
request()will tell you to userequestStream()if the operation contains@defer.requestStream()will tell you to userequest()if the operation is not streamable.headersaccepts a plain object, tuple array, orHeadersinstance.- The client sets
Accept: application/jsonautomatically, and upgrades it to multipart-compatible headers for deferred operations.
