@contoprix/graphql-client
v0.1.10
Published
Official Contoprix GraphQL delivery client — framework-neutral, zero runtime dependencies.
Readme
@contoprix/graphql-client
Framework-neutral GraphQL delivery client for Contoprix. It has no runtime dependencies beyond the platform fetch API and supports browsers and modern Node.js runtimes.
Use it for the stable system roots (site, page, pageById, and navigation) or for tenant-specific queries typed from @contoprix/codegen.
Installation
npm install @contoprix/graphql-clientAuthentication
The public GraphQL endpoint requires the graphql:read scope. A delivery key granted only delivery:read for REST calls will be rejected.
import { createContoprixGraphQLClient } from "@contoprix/graphql-client";
const client = createContoprixGraphQLClient({
endpoint: "https://cms.example.com",
locale: "en",
timeout: 15_000,
auth: {
type: "deliveryKey",
deliveryKey: process.env.CONTOPRIX_GRAPHQL_KEY!
}
});endpoint is the Contoprix base URL. The client appends /graphql automatically.
You can also use a pre-obtained bearer token:
const client = createContoprixGraphQLClient({
endpoint: "https://cms.example.com",
auth: { type: "accessToken", accessToken }
});Stable convenience methods
const site = await client.getSite();
const page = await client.getPage({ path: "/about" });
const byId = await client.getPageById({ id: "PAGE_ID", locale: "fr" });
const navigation = await client.getNavigation({ locale: "en" });The client-level locale is used when a convenience method does not provide one.
Run a custom query
Tenant content roots vary by schema, so use request<T> for them:
interface ArticleQueryResult {
article: {
__typename: "Article";
id: string;
title: string;
slug: string;
} | null;
}
const document = `
query Article($slug: String!, $locale: String) {
article(slug: $slug, locale: $locale) {
__typename
id
title
slug
}
}
`;
const data = await client.request<ArticleQueryResult>(document, {
slug: "welcome",
locale: "en"
});For real projects, run contoprix graphql sync and import the generated schema types instead of hand-writing them.
Partial GraphQL results
request() throws when a 200 response contains GraphQL errors, even if the response also contains partial data. Use requestWithErrors() when partial rendering is intentional:
const { data, errors } = await client.requestWithErrors<ArticleQueryResult>(
document,
{ slug: "welcome", locale: "en" }
);
if (errors.length > 0) {
console.warn(errors);
}
if (data?.article) {
console.log(data.article.title);
}Transport, timeout, network, and cancellation failures still throw because no usable GraphQL response exists.
Cancellation and timeouts
const controller = new AbortController();
const request = client.request<ArticleQueryResult>(
document,
{ slug: "welcome" },
{ signal: controller.signal }
);
controller.abort();
await request;A request-specific signal is combined with the client timeout; either one can cancel the fetch.
Error handling
import { ContoprixGraphQLError } from "@contoprix/graphql-client";
try {
await client.request(document, { slug: "missing" });
} catch (error) {
if (error instanceof ContoprixGraphQLError) {
console.error({
message: error.message,
statusCode: error.statusCode,
code: error.code,
errors: error.errors
});
}
}code is populated from the first GraphQL error extension when the server supplies a stable error code.
Custom headers and debugging
const client = createContoprixGraphQLClient({
endpoint: "https://cms.example.com",
auth: { type: "deliveryKey", deliveryKey: "..." },
headers: { "x-correlation-id": crypto.randomUUID() },
debug: true
});Do not enable debug logging if your surrounding logging could expose sensitive variables.
License
MIT
