Have found that the generated JSON Schema is incorrect when there is a z.union containing z.null.
This a test file example to reproduce the issue:
import fastifySwagger from '@fastify/swagger';
import Fastify, { type FastifyInstance } from 'fastify';
import fastifySwaggerUI from '@fastify/swagger-ui';
import plugin from 'fastify-plugin';
import {
serializerCompiler,
validatorCompiler,
jsonSchemaTransform as transform,
jsonSchemaTransformObject as transformObject,
type ZodTypeProvider,
} from 'fastify-type-provider-zod';
import { describe, it } from 'node:test';
import { z } from 'zod/v4';
import { strictEqual } from 'node:assert/strict';
const swagger = plugin(
async (app) => {
app.register(fastifySwagger, {
openapi: { info: { title: 'test', version: '0.0.1' } },
transform,
transformObject,
});
app.register(fastifySwaggerUI, { routePrefix: '/documentation' });
},
{ name: 'pluginSwagger' },
);
const valueSchema = z.union([z.null(), z.array(z.string()), z.literal('any')]);
describe('Swagger middleware', () => {
const app = Fastify();
app.setValidatorCompiler(validatorCompiler);
app.setSerializerCompiler(serializerCompiler);
const routes = async (router: FastifyInstance) => {
router.post(
'/test',
{
schema: {
response: {
200: valueSchema,
},
},
},
async (request, reply) => {
return reply.status(200).send(request.body);
},
);
};
const zodTypeProvider = plugin(
async (appInstance) => appInstance.withTypeProvider<ZodTypeProvider>().register(routes),
{ name: 'pluginRoutes' },
);
app.register(swagger);
app.register(zodTypeProvider);
describe('given a basic route with a zodv4 array', () => {
it('should return the correct swagger documentation', async () => {
// fetch documentation
const docResponse = await app.inject({ url: '/documentation/json' });
const docData = await docResponse.json();
strictEqual(docResponse.statusCode, 200);
console.log('fastify-zod: ', JSON.stringify(docData));
console.log('zod.toJSONSchema: ', JSON.stringify(z.toJSONSchema(valueSchema)));
});
});
});
What is happening:
Fastify zod is generating this document
{
"openapi": "3.0.3",
"info": { "title": "test", "version": "0.0.1" },
"components": { "schemas": {} },
"paths": {
"/test": {
"post": {
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": { "type": "array", "nullable": true }
}
}
}
}
}
}
}
}
Notice how it converted the union type into { "type": "array", "nullable": true }, where the items property is missing.
What I expect it should do:
I believe that the z.toJSONSchema behaviour is correct here, since it returns this:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"anyOf": [
{ "type": "null" },
{ "type": "array", "items": { "type": "string" } },
{ "type": "string", "const": "any" }
]
}
Here the union is properly represented.
Versions used:
"fastify-type-provider-zod": "5.0.2",
"zod": "3.25.76"
Have found that the generated JSON Schema is incorrect when there is a
z.unioncontainingz.null.This a test file example to reproduce the issue:
What is happening:
Fastify zod is generating this document
{ "openapi": "3.0.3", "info": { "title": "test", "version": "0.0.1" }, "components": { "schemas": {} }, "paths": { "/test": { "post": { "responses": { "200": { "description": "Default Response", "content": { "application/json": { "schema": { "type": "array", "nullable": true } } } } } } } } }Notice how it converted the union type into
{ "type": "array", "nullable": true }, where theitemsproperty is missing.What I expect it should do:
I believe that the
z.toJSONSchemabehaviour is correct here, since it returns this:{ "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ { "type": "null" }, { "type": "array", "items": { "type": "string" } }, { "type": "string", "const": "any" } ] }Here the union is properly represented.
Versions used: