-
-
Notifications
You must be signed in to change notification settings - Fork 210
Description
Prerequisites
- I have written a descriptive issue title
- I have searched existing issues to ensure the bug has not already been reported
Fastify version
5.2.2
Plugin version
6.0.1
Node.js version
22.14.0
Operating system
Windows
Operating system version (i.e. 20.04, 11.3, 10)
10
Description
Bug Description
Custom AJV formats configured in serializerOpts
work correctly when used directly in response schemas, but fail when the same schema is used within a discriminated union (TypeBox Type.Union
).
Configuration
The custom objectId
format is configured in both AJV validation and serialization:
const server: FastifyInstance = fastify({
ajv: {
customOptions: {
coerceTypes: 'array',
removeAdditional: 'all',
useDefaults: true,
formats: {
objectId: {
type: 'string',
validate: (value) => ObjectId.isValid(value),
},
},
keywords: ['transform'],
},
},
serializerOpts: {
ajv: {
formats: {
objectId: {
type: 'string',
validate: (value) => ObjectId.isValid(value),
},
},
},
},
});
Schema Definition
import { Type, Static } from '@sinclair/typebox';
import { ObjectId } from 'mongodb';
const BaseShapeSchema = Type.Object({
_id: Type.Unsafe<ObjectId>({
type: 'string',
format: 'objectId',
}),
name: Type.String(),
});
const CircleSchema = Type.Intersect([
BaseShapeSchema,
Type.Object({
type: Type.Literal('circle'),
radius: Type.Number(),
}),
]);
const RectangleSchema = Type.Intersect([
BaseShapeSchema,
Type.Object({
type: Type.Literal('rectangle'),
width: Type.Number(),
height: Type.Number(),
}),
]);
// Discriminated union
const ShapeSchema = Type.Union([CircleSchema, RectangleSchema]);
Expected Behavior
Both route configurations should serialize successfully and return the ObjectId as a string.
Actual Behavior
✅ Works: Direct schema usage
server.route({
method: 'GET',
url: '/test',
schema: {
response: {
200: Type.Object({
shape: RectangleSchema, // Direct schema reference
}),
},
},
handler: async (request, reply) => {
const rectangle = {
type: 'rectangle',
name: 'My Rectangle',
_id: new ObjectId('507f1f77bcf86cd799439015'),
width: 20,
height: 15,
};
return reply.status(200).send({ shape: rectangle });
},
});
Response: ✅ Success
{
"shape": {
"name": "My Rectangle",
"_id": "507f1f77bcf86cd799439015",
"type": "rectangle",
"width": 20,
"height": 15
}
}
❌ Fails: Discriminated union usage
server.route({
method: 'GET',
url: '/test',
schema: {
response: {
200: Type.Object({
shape: ShapeSchema, // Union schema reference
}),
},
},
handler: async (request, reply) => {
const rectangle = {
_id: new ObjectId('507f1f77bcf86cd799439015'),
name: 'My Rectangle',
type: 'rectangle',
width: 20,
height: 15,
};
return reply.status(200).send({ shape: rectangle });
},
});
Error: ❌ Failure
TypeError: The value of '#/properties/shape' does not match schema definition.
Analysis
The issue appears to be that when using discriminated unions (Type.Union
), the custom format is not being applied correctly by the serializer. The same schema works when referenced directly but fails when wrapped in a union type.
Link to code that reproduces the bug
https://github.com/fastify/fast-json-stringify
Expected Behavior
Both route configurations should serialize successfully and return the ObjectId as a string.