graft. docsbeta

Next.js

Typed reads in Server Components, React.cache, MdxBody, and withGraft.

pnpm add @usegraft/sdk-next
npm i @usegraft/sdk-next
bun add @usegraft/sdk-next

createGraft wraps the read client with React.cache, so repeated reads of the same document within one render are deduped. Server-only: the handle holds a database connection or an open index.

Create the handle

graft init scaffolds the static index. Pass that artifact and nothing else has to be running:

// lib/graft.ts
import { openStaticIndex } from "@usegraft/db";
import { createGraft } from "@usegraft/sdk-next";
import { collections } from "@/graft.config";

export const graft = createGraft({
  index: await openStaticIndex(".graft/index.db"),
  collections,
});

On Postgres, swap index for db:

import { createDb } from "@usegraft/db";
import { createGraft } from "@usegraft/sdk-next";
import { collections } from "@/graft.config";

export const graft = createGraft({
  db: createDb(process.env.DATABASE_URL!).db,
  collections,
});

Read one document

// in a Server Component
const page = await graft.getContent("pages", "home");

Return types come from your defineCollection schemas. A renamed field is a build error rather than a runtime undefined.

Render MDX

import { MdxBody } from "@usegraft/sdk-next";
import { mdxComponents } from "@/components/mdx-components";

<MdxBody source={page.body} components={mdxComponents} />;

MdxBody is Next-only. It defaults to trust: "restricted", which refuses {…} expressions, import, export, and spread attributes. Pass trust="full" only when every author has commit access, and set export const mdxTrust = "full" in graft.config.ts to match.

Mount functions and MCP

// app/api/fn/[name]/route.ts
export const POST = functionsHandler;

MCP mounts the same way with createGraftMcpHandler. Next's App Router takes the handler directly — there is no graftRoute wrapper here.

Cache and invalidation

import { revalidateContent, updateContent } from "@usegraft/sdk-next";

// in a route handler, after a compile webhook
revalidateContent(branch, changes);

// in a Server Action, for read-your-own-writes
updateContent(branch, changes);

Both turn a compile's ChangeSet into the exact revalidateTag / updateTag calls that refresh the changed pages, and no others. A no-op unless your reads were cached with 'use cache' and cacheTag, but always safe to call.

The caveat you cannot infer

Wrap the Next config with withGraft so @usegraft/registry stays external to the server bundle. Forgetting it breaks list_registry / describe_item at runtime with no build-time error.

// next.config.ts
import { withGraft } from "@usegraft/sdk-next/config";

export default withGraft({
  // your Next config
});

See SDK reference for mounts across adapters and the honest gaps.