Next.js: add one endpoint

Add ContractSpec to an existing Next.js App Router project by wiring a single operation end-to-end with validation and types.

What you'll build

  • One OperationSpec with SchemaModel validation.
  • A registry wiring the operation to its handler.
  • A Next.js route handler that exposes the operation.

1) Install core packages

nextjs-install
bun add -D contractspec
bun add @contractspec/lib.contracts @contractspec/lib.schema

Expected output: added 3 packages and a lockfile update.

2) Define the operation spec

Create src/contracts/healthcheck.operation.ts:

src/contracts/healthcheck.operation.ts
import { defineQuery } from "@contractspec/lib.contracts/operations";
import { SchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";

const HealthcheckOutput = new SchemaModel({
  name: "HealthcheckOutput",
  fields: {
    status: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
    checkedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
  },
});

export const HealthcheckQuery = defineQuery({
  meta: {
    key: "healthcheck.get",
    version: "1.0.0",
    description: "Return service health status",
    owners: ["@platform.core"],
    tags: ["health", "observability"],
  },
  io: {
    input: null,
    output: HealthcheckOutput,
  },
  policy: {
    auth: "public",
  },
  transport: {
    rest: { method: "GET" },
    gql: { field: "healthcheck_get" },
    mcp: { toolName: "healthcheck.get.v1" },
  },
});

3) Register the operation

Create src/contracts/registry.ts:

src/contracts/registry.ts
import {
  OperationSpecRegistry,
  installOp,
} from "@contractspec/lib.contracts/operations";
import { HealthcheckQuery } from "./healthcheck.operation";

export const registry = new OperationSpecRegistry();

installOp(registry, HealthcheckQuery, async () => ({
  status: "ok",
  checkedAt: new Date().toISOString(),
}));

4) Add the Next.js handler

Create app/api/ops/[...route]/route.ts:

app/api/ops/[...route]/route.ts
import { makeNextAppHandler } from "@contractspec/lib.contracts/server/rest-next-app";
import { registry } from "@/contracts/registry";

const handler = makeNextAppHandler(registry, async () => ({
  actor: "public",
  tenantId: "public",
}));

export { handler as GET, handler as POST };

Expected output: the route responds at /api/ops/healthcheck.get.

5) Validate the spec

nextjs-validate
contractspec validate src/contracts/healthcheck.operation.ts

Expected output: Validation passed.

Example package

Use @contractspec/example.lifecycle-dashboard as a lightweight Next.js snippet reference.

nextjs-example
# Inspect the example docs
contractspec examples show lifecycle-dashboard

# Validate all example manifests in this repo
contractspec examples validate --repo-root .

Need team approvals for new endpoints?

Studio adds review gates, shared registries, and audit trails after you ship your first endpoint.

Join Studio waitlist