ZZuro Docs

API Docs

Published Oct 8, 2025 | Updated Mar 13, 2026

Add OpenAPI + Scalar API documentation to your backend

Overview

Add production-ready OpenAPI generation and interactive API docs to your backend.


Install

npx zuro add docs

Scaffolds OpenAPI + Scalar routes and mounts API docs under /api/docs.


What This Generates

src/
├ lib/
│ └ openapi.ts
├ routes/
│ └ docs.routes.ts
└ routes/index.ts   # updated to mount /docs
  • src/lib/openapi.ts: OpenAPI registry + document generator.
  • src/routes/docs.routes.ts: serves JSON spec and Scalar UI.
  • src/routes/index.ts: mounts docs routes at /api/docs.

Quick Example

npx zuro add docs
npm run dev

Example request:

GET /api/docs/openapi.json

Example response:

{
  "openapi": "3.0.3",
  "info": {
    "title": "Zuro API",
    "version": "1.0.0"
  },
  "paths": {
    "/health": {
      "get": {}
    }
  }
}

Open interactive docs at http://localhost:3000/api/docs.


How It Works

  1. Client opens /api/docs.
  2. Scalar UI loads and requests /api/docs/openapi.json.
  3. createOpenApiDocument() builds the spec from the OpenAPI registry.
  4. Spec is returned and rendered in the docs UI.

Configuration

# No required environment variables
  • docs is code-configured in src/lib/openapi.ts.

API Reference

  • GET /api/docs: interactive Scalar docs UI.
  • GET /api/docs/openapi.json: generated OpenAPI spec.
  • createOpenApiDocument(): exports the OpenAPI document builder.

Advanced Usage

Register custom endpoints in src/lib/openapi.ts:

import { z } from "zod";

const createPostSchema = z.object({
  title: z.string().min(1),
  content: z.string().min(1),
});

registry.registerPath({
  method: "post",
  path: "/api/posts",
  tags: ["Posts"],
  request: {
    body: {
      content: {
        "application/json": {
          schema: createPostSchema,
        },
      },
    },
  },
  responses: {
    201: { description: "Post created" },
  },
});

Integrate module docs automatically:

npx zuro add docs
npx zuro add auth
npx zuro add uploads

When installed together, auth/uploads endpoints are injected into openapi.ts.


Example Use Cases

  • Internal API reference for backend teams.
  • Client SDK generation from OpenAPI JSON.
  • QA/UAT testing with interactive endpoint docs.
  • Publishing auth and upload workflows in one API portal.

Registering Your Routes

All endpoints need to be registered in src/lib/openapi.ts to appear in the spec. Use registry.registerPath() with Zod schemas for request/response shapes:

import { z } from "zod";
import { registry } from "./openapi";

// Register a GET endpoint
registry.registerPath({
  method: "get",
  path: "/api/users/{id}",
  tags: ["Users"],
  request: {
    params: z.object({ id: z.string().uuid() }),
  },
  responses: {
    200: {
      description: "User found",
      content: {
        "application/json": {
          schema: z.object({
            id: z.string(),
            email: z.string().email(),
            name: z.string().nullable(),
          }),
        },
      },
    },
    404: { description: "User not found" },
  },
});

Register a POST endpoint with a request body:

const createUserBody = z.object({
  email: z.string().email(),
  name: z.string().min(1),
  password: z.string().min(8),
});

registry.registerPath({
  method: "post",
  path: "/api/users",
  tags: ["Users"],
  request: {
    body: {
      content: {
        "application/json": { schema: createUserBody },
      },
    },
  },
  responses: {
    201: { description: "User created" },
    422: { description: "Validation error" },
  },
});

Adding Authentication to Docs

If your API uses Bearer token auth (JWT), register a security scheme so the Scalar UI shows an Authorize button:

// src/lib/openapi.ts
registry.registerComponent("securitySchemes", "bearerAuth", {
  type: "http",
  scheme: "bearer",
  bearerFormat: "JWT",
});

Then apply it to protected routes:

registry.registerPath({
  method: "get",
  path: "/api/users/me",
  tags: ["Users"],
  security: [{ bearerAuth: [] }],
  responses: {
    200: { description: "Current user profile" },
    401: { description: "Unauthorized" },
  },
});

The Scalar UI will show a lock icon on secured routes and an Authorize dialog for entering tokens.


Generating a Client SDK

With the OpenAPI spec available at /api/docs/openapi.json, you can auto-generate a typed client:

# Using openapi-typescript
npx openapi-typescript http://localhost:3000/api/docs/openapi.json -o src/types/api.d.ts

Or generate a full fetch client:

# Using hey-api
npx @hey-api/openapi-ts \
  --input http://localhost:3000/api/docs/openapi.json \
  --output src/client \
  --client fetch

Troubleshooting

Routes not appearing in the spec You must call registry.registerPath() in src/lib/openapi.ts — simply creating Express routes does not auto-populate the spec. Zuro injects auth and upload routes automatically when those modules are installed together.

/api/docs returns 404 after adding the module Ensure the docs routes are mounted. Check src/routes/index.ts for:

import docsRouter from "./docs.routes";
router.use("/docs", docsRouter);

Zuro injects this automatically, but manual route files may need it added.

Scalar UI blank / not loading Scalar loads its UI assets from a CDN. In environments without internet access (air-gapped, some CI), the UI will be blank. The JSON spec at /api/docs/openapi.json still works and can be imported into Postman or Insomnia.

Schema too large / slow to generate If you have hundreds of registered paths, move createOpenApiDocument() to a cached singleton so it's only built once per server start rather than on every request.

On this page