Next.js
Add Checkrd policy enforcement to Next.js Route Handlers and Server Actions.
Next.js
Checkrd ships first-class Next.js helpers for both the App Router (Route Handlers) and Server Actions, with the right initialization shape for the Edge Runtime and Node runtime simultaneously.
Install
npm install checkrdInitialize once
Next.js bundles each route file separately. Initialize Checkrd at module scope so the WASM engine is loaded once per worker:
// app/lib/checkrd.ts
import { initCheckrd } from "checkrd/integrations/next";
export const checkrd = initCheckrd({
policy: "/var/run/secrets/policy.yaml",
agentId: "web",
});initCheckrd is idempotent across hot reloads and works in both Node and Edge runtimes (it uses initAsync under the hood). Tests can call resetCheckrdNext() between cases.
Route Handlers (App Router)
// app/api/chat/route.ts
import { checkrdRoute } from "checkrd/integrations/next";
import { checkrd } from "@/lib/checkrd";
export const POST = checkrdRoute(checkrd, async (req) => {
const { messages } = await req.json();
// ... your handler ...
return Response.json({ result: "..." });
});checkrdRoute wraps the handler so every inbound request is policy-evaluated before the body runs. On deny, returns a 403 with the error envelope:
{
"error": {
"type": "policy_denied",
"message": "deny rule 'block-pii' fired",
"request_id": "...",
"dashboard_url": "https://app.checkrd.io/events/..."
}
}Server Actions
// app/actions.ts
"use server";
import { checkrdAction } from "checkrd/integrations/next";
import { checkrd } from "@/lib/checkrd";
export const summarize = checkrdAction(checkrd, async (input: string) => {
return { summary: input.slice(0, 100) };
});checkrdAction wraps a Server Action so the policy is evaluated against the action call before the function body runs.
What gets enforced
| Helper | Synthetic URL | Body |
|---|---|---|
checkrdRoute | {request.method} {request.url} | The request body (JSON or raw) |
checkrdAction | next-action://{actionName} | The action arguments |
You write rules against these the same as any other Checkrd rule.
agent: web
default: deny
rules:
- name: allow-public-chat
allow:
url: "POST */api/chat"
- name: allow-summarize-action
allow:
url: "next-action://summarize"Pages Router (legacy)
The same helpers work for Pages Router API routes — wrap your default export with checkrdRoute. Recommended only for existing Pages Router apps; new projects should use App Router.
Edge Runtime
Both helpers detect the runtime automatically. In Edge, initCheckrd loads the WASM via fetch + WebAssembly.compile. No node:* imports happen at module load — the bundle is edge-clean. The tests/edge_runtime.test.ts regression test in the repo locks this in.
Caveats
initCheckrdis module-scoped. Don't call it inside the handler body — the engine should be reused across requests for sub-millisecond evaluation.- Environment variables. In Edge Runtime, only
process.env.CHECKRD_API_KEYand friends declared innext.config.tsare visible. Pass them explicitly when needed. - Server Actions arguments are not always JSON-serializable. The adapter uses
JSON.stringifywith a fallback toString(value); complex objects (Dates, Buffers, FormData) lose detail in body matchers.