OpenAPI
Auto-generated OpenAPI 3.1 spec from your router — per-procedure metadata, path parameters, security, and custom overrides.
Silgi generates a full OpenAPI 3.1.0 specification from your router automatically. Every procedure becomes an operation — input schemas become request bodies or query parameters, output schemas become response bodies, and typed errors become error responses.
Enable
Pass scalar: true to serve() or handler():
s.serve(appRouter, {
: true,
})This serves:
/openapi.json— the raw OpenAPI specification/reference— interactive API docs powered by Scalar
Procedure metadata
Use $route() to add OpenAPI metadata to any procedure:
const = s
.$route({
: 'GET',
: '/users/:id',
: 'Get user by ID',
: 'Returns a single user by their numeric ID.',
: ['Users'],
: 'getUserById',
: false,
: 200,
: 'User found',
})
.$input(z.object({ : z.number() }))
.$output(UserSchema)
.$errors({ : 404 })
.$resolve(...)| Option | Type | Default | Description |
|---|---|---|---|
method | string | 'POST' | HTTP method (GET, POST, PUT, DELETE, etc.) |
path | string | auto from router | Custom URL path (supports :param, :param(regex), **) |
summary | string | — | Short summary shown in API docs |
description | string | — | Detailed description (supports markdown) |
tags | string[] | auto from router | Tags for grouping operations |
operationId | string | auto from router | Custom operation ID for SDK generation |
deprecated | boolean | — | Mark as deprecated |
successStatus | number | 200 | HTTP status code for success response |
successDescription | string | 'Successful response' | Description for success response |
Path parameters
:param syntax is automatically converted to OpenAPI {param} format with proper parameter declarations:
const = s
.$route({ : 'GET', : '/users/:id' })
.$input(z.object({ : z.number() }))
.$resolve(...)
// Spec: GET /users/{id}
// parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }]Supported patterns:
:id— required path parameter:id(\\d+)— parameter with regex constraint:id?— optional parameter**— catch-all wildcard (becomes{path})
Security
Global security
Set a global security scheme in ScalarOptions:
s.serve(appRouter, {
: {
: {
: 'http',
: 'bearer',
: 'JWT',
},
},
})All operations will require this scheme by default.
Per-procedure override
Override security on individual procedures:
// Public endpoint — no auth required
const = s.$route({ : false }).$resolve(() => ({ : 'ok' }))
// Requires specific scheme
const = s.$route({ : ['bearerAuth'] }).$resolve(() => ({ : true }))| Value | Effect |
|---|---|
false | Public — security: [] in spec |
string[] | Named security schemes required |
| not set | Inherits global security |
Error responses
Typed errors
Errors declared with $errors() are automatically documented:
const = s
.$errors({
: 404,
: { : 403, : 'Not allowed' },
})
.$resolve(...)
// Spec generates: 404 and 403 response entries with error schemaThe message field appears as default in the generated schema.
Validation errors
Procedures with $input() automatically get a 400 BAD_REQUEST response in the spec with the validation error schema — no configuration needed.
Guard errors
Errors from guards ($use(authGuard)) are merged into the procedure's error responses:
const = s.guard({
: { : 401 },
: () => { ... },
})
const = s
.$use()
.$errors({ : 403 })
.$resolve(...)
// Spec: 401 from guard + 403 from procedureCustom OpenAPI override
For anything not covered by the built-in options, use spec to override or extend the generated operation:
Object merge
s.$route({
: {
: { : 'https://docs.example.com/users' },
'x-rate-limit': 100,
'x-internal': true,
},
})Function override
Receives the auto-generated operation, return the final one:
s.$route({
: () => ({
...,
'x-codegen-request-body-name': 'body',
: [
...(.parameters ?? []),
{ : 'X-Tenant-ID', : 'header', : true, : { : 'string' } },
],
}),
})The spec override is applied last — after all auto-generation is complete. It's the escape hatch for any OpenAPI
feature not directly supported.
Schema conversion
Silgi uses Standard Schema to convert your validator schemas (Zod, Valibot, ArkType) to JSON Schema. This happens via the ~standard.jsonSchema.input() interface — no Zod-specific introspection needed.
This means the generated OpenAPI spec is accurate for any validator that implements Standard Schema v1, not just Zod.
Tags
Tags are auto-generated from the first router segment:
const = s.router({
: { // tag: "users"
: ...,
: ...,
},
: { // tag: "orders"
: ...,
},
})Override with $route({ tags: ['Custom', 'Tags'] }).
What's next?
- Server — Scalar UI configuration and CDN options
- Procedures —
$input(),$output(),$errors()API - Middleware — guards with typed errors