@davaux/swagger

Generates an OpenAPI 3.0 spec from your route files and serves a Swagger UI at /docs. Works with any Davaux app that uses defineHandler API routes — no annotations or decorators required.

Installation

npm install @davaux/swagger

Setup

Add it as middleware in src/middleware.ts so it runs before route dispatch:

// src/middleware.ts
import { defineMiddleware } from 'davaux'
import { swagger } from '@davaux/swagger'

const swaggerMw = swagger({
  routesDir: process.env.NODE_ENV === 'production' ? './dist' : './.davaux/routes',
  info: {
    title: 'My API',
    version: '1.0.0',
    description: 'REST API built with Davaux',
  },
})

export default defineMiddleware((ctx, next) => swaggerMw(ctx, next))

Then visit /docs in your browser while running davaux dev or davaux start.

Options

OptionTypeDefaultDescription
routesDirstringDirectory containing compiled route files (see below)
info.titlestring'API'OpenAPI info title
info.versionstring'1.0.0'OpenAPI info version
info.descriptionstringOptional description
pathstring'/docs'URL for the Swagger UI page
specPathstring'/openapi.json'URL for the raw OpenAPI JSON

routesDir

Points to the directory of compiled .js route files that @davaux/swagger introspects. The correct path depends on your environment:

ModeroutesDir
davaux dev./.davaux/routes
davaux start./dist

The process.env.NODE_ENV check shown in the setup example is the simplest way to handle both automatically.

What gets documented

Only defineHandler API routes are included. Page routes (.page.tsx) serve HTML and are skipped. Every API route becomes an operation in the spec:

  • URL pattern → OpenAPI path (/api/users/:id/api/users/{id})
  • Route type → HTTP method (.get.tsGET, .post.tsPOST, etc.)
  • URL params → path parameter objects with in: 'path'
  • Catch-all params ([...slug]) → {slug} path parameters

Zod schema extraction

If zod-to-json-schema is installed, @davaux/swagger automatically converts named *Schema exports from your route files into OpenAPI request body schemas:

npm install zod-to-json-schema
// src/routes/api/users.post.ts
import { defineHandler } from 'davaux'
import { z } from 'zod'

// Named export ending in "Schema" — picked up automatically
export const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  role: z.enum(['admin', 'member']).default('member'),
})

export default defineHandler(async (ctx) => {
  const input = await ctx.json(CreateUserSchema)
  // ...
})

The spec will include the Zod schema as a JSON Schema requestBody on the POST operation. If zod-to-json-schema is not installed, the request body is simply omitted — no error is thrown.

Raw spec

The OpenAPI JSON is available at /openapi.json (or your custom specPath). Use it with any OpenAPI tooling — code generators, Postman, Insomnia, etc.:

curl http://localhost:3000/openapi.json | jq .

The spec is generated once on the first request and cached for the lifetime of the process. Restart the server after adding or removing routes.

Restricting access

In production you may want to put the docs behind authentication. Do it in src/middleware.ts before the swagger middleware:

import { defineMiddleware } from 'davaux'
import { swagger } from '@davaux/swagger'

const swaggerMw = swagger({ routesDir: './dist', info: { title: 'My API', version: '1.0.0' } })

export default defineMiddleware(async (ctx, next) => {
  const path = ctx.req.url?.split('?')[0]
  if (path === '/docs' || path === '/openapi.json') {
    if (ctx.req.headers['x-docs-token'] !== process.env.DOCS_TOKEN) {
      ctx.res.writeHead(401, { 'Content-Type': 'text/plain' })
      ctx.res.end('Unauthorized')
      return
    }
  }
  return swaggerMw(ctx, next)
})