← Back to nodox

Automated OpenAPI Generation
from Zod Schemas in Express

Stop duplicating your Zod type definitions into Swagger YAML. nodox-cli reads your existing z.object() schemas directly from your Express routes and generates live OpenAPI docs automatically.

Get started: npx nodox init

The duplication problem

You've already defined every field in Zod for runtime validation. The moment you also need Swagger docs, most tools force you to define those fields again: in YAML, JSDoc, or a separate schema registry. That's two sources of truth that diverge every time you rename a field.

Without nodox-cli (manual duplication)

// Zod schema
const CreateUser = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().optional(),
})

// Then ALSO write this in YAML...
// paths:
//   /users:
//     post:
//       requestBody:
//         content:
//           application/json:
//             schema:
//               type: object
//               properties:
//                 name:
//                   type: string
//                   minLength: 1
//                 email:
//                   type: string
//                   format: email
//                 age:
//                   type: integer
// ... 30 more lines

With nodox-cli (zero duplication)

import nodox from 'nodox-cli'
import { z } from 'zod'

const app = express()
app.use(express.json())
app.use(nodox(app))   // ← one line, that's it

const CreateUser = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().optional(),
})

// nodox reads the Zod schema automatically; no extra code needed
app.post('/users', (req, res) => {
  const result = CreateUser.safeParse(req.body)
  if (!result.success) return res.status(400).json({ error: result.error })
  res.status(201).json({ id: 1, ...result.data })
})

// Done. Visit /__nodox for live docs.

What nodox-cli detects from Zod

  • Primitive types: z.string(), z.number(), z.boolean(), z.date()
  • Constraints: .min(), .max(), .email(), .url(), .uuid(), .regex()
  • Optional fields: .optional(), .nullable()
  • Nested objects: z.object({ address: z.object({...}) })
  • Arrays: z.array(z.string())
  • Enums: z.enum(['admin', 'user'])
  • Unions: z.union([...])
  • Response schemas: validate(Input, { responses: { 200: Output } })
  • Zod v3 and v4

Setup in 30 seconds

npm install nodox-cli npx nodox init Full setup guide →

Also supports

nodox-cli isn't Zod-only. It reads Joi, express-validator, and yup schemas with the same zero-config approach.