Configure one client, then reuse it#
Create the Contoprix client once in a server-side module. This gives every request the same base URL, authentication method, timeout, and default language.
environment variables → ContoprixClient → data mapper → page/componentDo not create a new client inside every presentational component, and do not put a management secret in browser code.
1. Add environment variables#
For a frontend that reads published content, use a delivery key:
CONTOPRIX_BASE_URL=https://cms.example.com
CONTOPRIX_DELIVERY_KEY=replace-with-your-delivery-keyCONTOPRIX_BASE_URL is the CMS API origin. The client adds paths such as /api/delivery/content/... itself, so do not append /api.
Important
Never use NEXT_PUBLIC_CONTOPRIX_DELIVERY_KEY, NEXT_PUBLIC_CONTOPRIX_CLIENT_SECRET, or a similar public variable for a credential. In Next.js, NEXT_PUBLIC_ values are bundled into browser JavaScript.
2. Create the shared client#
import "server-only";
import { ContoprixClient } from "@contoprix/client";
const baseUrl = process.env.CONTOPRIX_BASE_URL;
const deliveryKey = process.env.CONTOPRIX_DELIVERY_KEY;
if (!baseUrl || !deliveryKey) {
throw new Error("CONTOPRIX_BASE_URL and CONTOPRIX_DELIVERY_KEY are required.");
}
export const client = new ContoprixClient({
baseUrl,
auth: {
type: "deliveryKey",
deliveryKey,
},
languageCode: "en",
timeout: 30_000,
});The languageCode is the default for calls that do not specify a language. A per-request language always overrides it.
Choose the right authentication method#
| Auth type | Good for | What the SDK sends | Where it belongs |
|---|---|---|---|
deliveryKey | Published delivery for one website | x-contoprix-delivery-key | Server-side delivery code; use a tightly scoped key |
clientCredentials | Trusted server-to-server SDK operations | The SDK exchanges ID and secret for a bearer token | Server-side only |
accessToken | An already authenticated protected workflow | Authorization: Bearer ... | Server-side or a protected backend boundary |
Delivery key: normal public-site delivery#
auth: {
type: "deliveryKey",
deliveryKey: process.env.CONTOPRIX_DELIVERY_KEY!,
}This is the usual choice for a website that reads published pages and entries. The key determines the website context, so the SDK does not ask you to send a website ID on each request.
Client credentials: trusted service integration#
auth: {
type: "clientCredentials",
clientId: process.env.CONTOPRIX_CLIENT_ID!,
clientSecret: process.env.CONTOPRIX_CLIENT_SECRET!,
}The SDK obtains and caches a bearer token from the CMS token endpoint, refreshing it before it expires. Keep both values on a server. Client credentials are also what the CLI uses for schema operations, subject to its granted scopes.
Access token: use an existing bearer token#
auth: {
type: "accessToken",
accessToken: existingAccessToken,
}Use this only when your backend already has a valid token. The environment helper does not infer an access-token configuration for you; construct this form explicitly.
Fetch a page#
Use pages.get() for the root page and pages.getBySlug() for a named path:
import { client } from "./client";
export const getHomePage = () => client.pages.get({ languageCode: "en" });
export const getAboutPage = () =>
client.pages.getBySlug("/about", { languageCode: "en" });A delivered page has page metadata such as name, slug, languageCode, and an ordered blocks array. Its block data is rendered through a component registry; see Visual Builder Setup when you are ready to render CMS-composed pages.
Fetch one content entry by slug#
import { client } from "./client";
type ArticleData = {
title?: string;
summary?: string;
};
export async function getArticle(slug: string, languageCode = "en") {
const entry = await client.content.getBySlug("article", slug, {
languageCode,
});
const data = entry.data as ArticleData;
return {
id: entry.id,
slug: entry.slug,
languageCode: entry.languageCode,
title: data.title ?? "Untitled article",
summary: data.summary ?? "",
};
}The method signature is:
client.content.getBySlug(contentTypeCode, slug, { languageCode? })It requests one published entry. The content-type code and slug are normalized for delivery; use the values that exist in the CMS model and entry.
Fetch a list safely#
const result = await client.content.list({
contentType: "article",
languageCode: "en",
take: 12,
skip: 0,
sort: "newest",
});
const articles = result.items;
const { total, hasNext } = result.pagination;The delivery list supports a content-type code, language, take, skip, and newest or oldest sorting. take is limited to 100 by the delivery API. Although the SDK type has a filters property for future-compatible clients, the current REST delivery list does not apply arbitrary field filters; do not build a production feature that depends on them yet.
Override the language for a request#
Do not mutate the shared client just because one route needs another locale. Pass the locale on the call instead:
const frenchArticle = await client.content.getBySlug("article", "hello-contoprix", {
languageCode: "fr",
});The requested language must exist for the delivery-key website, and the matching entry must be published. Read Localization before adding locale routes.
Next.js server helpers#
@contoprix/next/server can create a client from the same server environment variables:
import { getContoprixPage } from "@contoprix/next/server";
const page = await getContoprixPage({
slug: "/about",
languageCode: "en",
});getContoprixContent() is also available when you already know the entry ID. For a lookup by slug, call createContoprixClient().content.getBySlug(...) or use your own shared client as shown above.
Keep SDK code server-side#
Use the client in a Server Component, route handler, server action, or backend service. Pass only the mapped data needed by an interactive Client Component.
Server: SDK request + credential + mapping
Browser: rendered props + user interactionThis protects credentials, makes failed delivery calls easier to handle consistently, and prevents each UI component from needing to understand the full CMS response.
Configuration checklist#
CONTOPRIX_BASE_URLis the API origin, without/api.- The delivery key belongs to the website that owns the pages and entries.
- The default
languageCodeis a configured website language. - All custom content fields are read from
entry.data. - A request-specific locale is passed on the SDK call, not stored in a mutable global.
- Delivery and preview credentials are kept in server-side code.
Next, use Content Types to design the data you will read, or Create your first project for the complete Article walkthrough.