ProductsFor your stack

SDK & CLI

Typed from your models. Down to the field.

@capacms/sdk is Capa's TypeScript client: REST and GraphQL reads typed from your schema, Next.js cache helpers that refresh on publish, the visual editing overlay, and a CLI for codegen and persisted queries.

app/blog/page.tsx0 problems
import { createClient } from "@capacms/sdk/next";
import type { CapaQuery } from "./capa-graphql";

const capa = createClient<CapaQuery>({ baseUrl, apiKey, version: "2026-10-01" });

const { data } = await capa.graphql.query({
  articles: {
    args: { first: 5, sort: ["publishedAt_DESC"] },
    nodes: { titeltitle: true, author: { name: true } },
  },
});

data?.articles?.nodes[0].title;
// data?.articles?.nodes[0].body would not compile: it was not selected
TS2322Type 'true' is not assignable to type 'true & SelectionError<"titel is not a field of articles.nodes">'.
(property) title: string | null
The client

Write the read. Let the types follow.

One client per key, the platform version pinned in code, and the result typed from exactly what you selected.

  • Typed reads

    Pass the types capa-codegen wrote and fields come back typed as the API returns them. Name a field you did not select and it fails to compile.

  • Next.js, cached and fresh

    graphql() keeps a published read in Next's data cache under one tag per model. A publish revalidates exactly those tags.

  • One webhook route

    Verify the signature, then revalidateFromWebhook refreshes the entry, model and media tags the change touched.

  • Errors you can branch on

    A refused request throws CapaError with the API's code, hint and docs link. get() returns null for a missing entry.

lib/capa.ts
import { createClient } from "@capacms/sdk/next";

const capa = createClient({
  baseUrl: process.env.CAPA_API_URL!,   // https://cdn.capacms.com
  apiKey: process.env.CAPA_KEY!,
  version: "2026-10-01",
});

const page = await capa.entries.list("articles", {
  select: ["title", "views", { author: ["name"] }],
  filter: { views: { gte: 10 }, tags: { hasAny: ["news", "launch"] } },
  sort: ["-views"],
  limit: 25,
});

for await (const entry of capa.entries.iterate("articles", { select: ["title"] })) {
  console.log(entry.fields.title);
}
The CLI

Three commands for your build.

The capa and capa-codegen commands ship in the package. They read the same CAPA_API_URL and CAPA_KEY as your app, from your shell or your .env files.

zsh · ~/site
$ capa-codegen --graphql --out src/capa-graphql.ts
# one typed module: every model, every #graphql document

$ capa persist --release "$GIT_COMMIT" --env production
835d679d63…  Latest  stored, pinned
1f0c9e2ab4…  BlogIndex  stored, pinned
a77e31c0d8…  ArticlePage  stored, pinned
capa persist: 3 of 3 documents stored, pinned.

$ capa-codegen --graphql --out src/capa-graphql.ts --check
# exit 0 when current, exit 2 when a model moved

  1. capa-codegen

    Types for every model and every GraphQL document in your project. --watch rewrites them as you edit.

  2. capa persist

    Registers each document by its sha256, pinned to the release, so the production key only ever sends a hash.

  3. --check in CI

    Fails the build with exit code 2 when a model changed and the committed types did not.

What is in the box

Five imports. Use only what you need.

ImportWhat it is
@capacms/sdk/nextThe client for /api/: entries, GraphQL, pages and preview. Start here.
@capacms/sdk/nextjsNext.js helpers: graphql() in a server component, cache tags, draft and edit mode, webhook revalidation.
@capacms/sdk/nextjs/overlayThe visual editing overlay as a Next.js client component.
@capacms/sdk/overlayThe same overlay without Next, for any framework.
@capacms/sdkThe client for the /v2 API your sites may already use, and the webhook signature check.

Node 18 or later. Types need TypeScript 5.0 or later, with strict on or off.

Made for real projects

The small things a big site needs.

  • Builder

    Queries as objects

    Write GraphQL as a typed object and misspelled fields, arguments and sort values fail to compile, each error naming where it was written.

  • Codegen

    Commit the output

    Generated types live in your repo, so a model change is a reviewable diff. Re-running when nothing moved costs one conditional request.

  • Fetch

    Plain fetch underneath

    The client returns plain data from plain fetch, so Next's cache, React Router loaders and your own wrappers work as they are.

  • Shape

    Flat when you want it

    Ask for shape=flat to get each related entry once, and inflate() turns it back into the nested tree.

  • Preview

    Edit mode built in

    capaAttrs tags what an editor can click. Visitors' pages carry no tags and never download the overlay.

  • Agents

    Same names as the MCP server

    The MCP server writes code against this SDK, with the same environment variables, so what an agent writes runs here.

One install. Typed all the way down.

pnpm add @capacms/sdk@next and your models are types.