Skip to navigation

Zod To OpenAPI

User Story:

User Story 712102 Spike: Evaluate use of Zod-to-OpenAPI for schema generation

Library:

https://github.com/asteasolutions/zod-to-openapi

Branch:

CLADevOps/fluxx-registration at jv_ZodToOpenAPI_2_712102

1. Can Zod-to-OpenAPI generate accurate and usable Swagger docs?

Yes, but only partly.
Zod-to-OpenAPI can generate proper OpenAPI 3.x docs from Zod schemas, including schema components, types, required fields, and example values.

It works well for JSON request/response bodies, but the registered paths (registerPath) don’t automatically show up in Fastify Swagger. You will need to manually add them or use a generator. So, it is good for documentation, but not a plug-and-play setup for Fastify validation.

2. Does it support multipart/form-data and file uploads?

Limited support in this version.
You can document uploads by marking fields as type: string, format: binary in .openapi() metadata, but it won’t automatically generate full multipart schemas or validate uploads properly.

3. How hard is it to integrate with Fastify Swagger?

hard.
.openapi() is meant for docs, not runtime validation. Using these directly in Fastify routes can trigger validation errors like data/required must be array.
To make paths appear in Swagger, inject paths manually into the Fastify Swagger config. Setup isn’t smooth.

4. Does it reduce duplication?

Yes, for schemas.
You only define fields, examples, and required properties once in Zod, which can be used for both validation and docs. However, path definitions still need manual work, so it does not remove duplication there.

5. Tradeoffs

AreaWhat’s goodWhat’s not
FlexibilityCan describe complex Zod schemas with examples and types.Multipart/form-data and file uploads still limited.
MaintainabilityOne source of truth for schema definitions.Paths are duplicated, you declare them in Fastify and again in registry. Easy to get out of sync.
Dev Experience.openapi() annotations are clean and readable.Manual registry setup adds extra work per route; error-prone for large services.

This is where paths are duplicated

Before (Fastify only)After (with Zod-to-OpenAPI)
Where definedfastify.route()fastify.route() + registerPath()
ImpactSingle sourceDuplicate paths to maintain
RiskNoneFastify vs Swagger mismatch

Before (Fastify only)

fastify.route({ method: ‘POST’, url: ‘/users’, schema: userSchema, handler: createUser, });

After (with Zod-to-OpenAPI)

fastify.route({ method: ‘POST’, url: ‘/users’, schema: userSchema, handler: createUser, });

registry.registerPath({ method: ‘POST’, path: ‘/users’, request: { body: userSchema }, responses: { 200: userResponse }, });


Things to consider

Recommendation

  • Keep plain Zod for Fastify runtime validation.