> [Agent navigation index](/llms.txt)

# API server contracts and routes

Define a universal typed API contract, implement its server-only route, and let Brix discover and register the endpoint.

## Define a contract

```ts
import { createApiContract } from "#brix";
import { z } from "brix/zod";

export const updateProfileApiContract = createApiContract({
  method: "POST",
  path: "/api/profiles/:profileId",
  body: z.object({ displayName: z.string().min(1) }),
  response: z.object({ id: z.string(), displayName: z.string() }),
});

```

Path parameters are inferred from `:profileId`. GET contracts cannot have a body. Body and query callers provide schema input; handlers receive parsed output. A response schema describes JSON wire input, while clients receive its parsed output.

API paths follow the same [portable route grammar as pages](/docs/pages#portable-route-paths): optional parameters are terminal, and constraints reject anchors, alternation, lookarounds, empty matches, and nonterminal slash-spanning patterns.

Use `.returns<Type>()` only when compile-time response typing is sufficient. Prefer a Zod `response` when external data should be checked at runtime.

Conventional routes live at `src/features/<feature>/api/<action>/route.ts` and export exactly one runtime value named `*ApiRoute`.

## Implement the server route

```ts
import { createApiRoute } from "#brix/server";
import { updateProfileApiContract } from "./contract.ts";

export const updateProfileApiRoute = createApiRoute(updateProfileApiContract, {
  auth: true,
  middleware: [rateLimitMiddleware],
  handler: async (_c, { body, params }) => {
    const profile = await saveProfile(params.profileId, body);
    return { id: profile.id, displayName: profile.displayName };
  },
});

```

Return plain JSON data; Brix normalizes, validates, and responds. `undefined` produces 204. Invalid successful output and unexpected failures reach the application's error policy. A deliberate Hono `HTTPException` passes through. `auth: true` requires the server authentication integration described in [server setup](/docs/server).

Routes are discovered automatically. Do not mount them manually or import `route.ts` into browser code. Server code can call `route.execute(c, options)` directly without HTTP or middleware; auth context and input/output types remain checked.

Continue with [call api contracts](/docs/api-routes-clients).
