# Route Builder

> Build and migrate API routes using the defineRoute() pattern. Use when creating new endpoints OR migrating existing endpoints to the standard route-builder syntax.

- Skill: `bighadj22/route-builder` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add bighadj22/route-builder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bighadj22/route-builder/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: bighadj22 (https://skillmd.com/u/bighadj22)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/bighadj22/route-builder

---


# Route Builder Pattern

`defineRoute()` is the **standard pattern** for all endpoints in cod-server. Every new endpoint must use it. **All existing domains are already migrated** — if you encounter a raw `createRoute()` route, convert it using the migration workflow.

Two workflows:
1. **Creating new endpoints** — see [NEW-ENDPOINTS.md](NEW-ENDPOINTS.md)
2. **Migrating existing endpoints** — see [MIGRATION.md](MIGRATION.md)

Real-world examples from the codebase: [EXAMPLES.md](EXAMPLES.md)

## Quick Reference

```typescript
import { defineRoute } from "@/lib/route-builder";
import { SCOPES } from "../../../../cod-shared/rbac/scopes";

const myRoute = defineRoute({
  method: "get",           // "get" | "post" | "patch" | "delete" | "put"
  path: "/my-resource",
  auth: { scope: SCOPES.RESOURCE_READ },
  tags: ["MyTag"],
  summary: "List resources",
  operationId: "listResources",
  query: MyFiltersSchema,  // optional
  params: MyParamsSchema,  // optional
  body: MyBodySchema,      // optional
  responses: {             // optional: only when default responses are not enough
    200: { description: "...", content: { "application/json": { schema: MySchema }}},
    422: { description: "Business rule violation" },
  },
  handler: handlers.myHandler,
});

router.openapi(myRoute.route, myRoute.handler);
```

## Auth Strategies

```typescript
auth: "public"                            // No auth — no middleware, security omitted from spec (webhook receivers)
auth: "api-key"                           // Any authenticated dashboard user
auth: "admin"                             // Admin role only (stores, users)
auth: "store"                             // Store API key (X-Store-API-Key) — store/*, abandoned-orders storefront
auth: { scope: SCOPES.ORDERS_READ }       // Specific scope (most common)
auth: { anyOf: [SCOPES.READ, SCOPES.ALL] }   // Any of multiple scopes
auth: { allOf: [SCOPES.READ, SCOPES.ADMIN] } // All scopes required
```

Non-JSON request bodies use `bodyContent` (raw content map):

```typescript
bodyContent: {
  "multipart/form-data": {
    schema: z.object({ file: z.instanceof(File).openapi({ type: "string", format: "binary" }) }),
  },
}
```

Header schemas (e.g. svix webhook receivers): `headers: MyHeadersSchema`.

## What `defineRoute()` generates automatically

- `middleware` — from `auth` strategy
- `security` — `ApiKeyAuth` or `StoreAuth`
- Standard error responses: 400, 401, 403, 404, 500
- `tags` — inferred from path if not provided
- `operationId` — from handler name if not provided

Override only what you need to customize via `responses: {...}`.

## Key Rules

1. **Always use SCOPES constants** — never magic strings
   - ✅ `auth: { scope: SCOPES.ORDERS_READ }`
   - ❌ `auth: { scope: "orders:read" }`

2. **Register specific paths before param paths**
   - ✅ `POST /bulk-dispatch` before `GET /{id}`
   - ✅ `GET /default/rules` before `GET /{id}`

3. **Schemas come from domain files in `@/openapi/schemas`**
   - ✅ `import { OrderListItemSchema } from "@/openapi/schemas"`
   - ❌ Inline anonymous schemas for entities that have domain schemas

4. **Always run typecheck + tests after migrating**
   ```bash
   cd cod-server && npm run typecheck && npm test
   ```

## Files

- [SKILL.md](SKILL.md) — this file
- [MIGRATION.md](MIGRATION.md) — how to migrate existing endpoints
- [NEW-ENDPOINTS.md](NEW-ENDPOINTS.md) — how to create new endpoints
- [EXAMPLES.md](EXAMPLES.md) — real examples from wilayas and orders

