← 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-cli 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 — this time 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, { validate } from 'nodox-cli'
import { z } from 'zod'

const app = express()
app.use(express.json())
app.use(nodox(app))   // ← one line

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

// validate() reads the Zod schema AND
// validates incoming requests
app.post('/users',
  validate(CreateUser),
  handler
)

// 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, { response: Output })
  • Zod v3 and v4

Setup in 30 seconds

npm install nodox-cli npx nodox-cli 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.