-
-
Notifications
You must be signed in to change notification settings - Fork 215
Description
Fastify version
5.5.0
Plugin version
9.5.1
Node.js version
22.14.0
Operating system
Windows 10
Description
When a route has a schema.body defined, Fastify requires that the request includes a body (at least {}), even if all properties inside that body are optional.
However, the OpenAPI schema generated by @fastify/swagger does not correctly reflect this requirement. Currently, requestBody.required is set to true only when there are required properties inside the object. If the object has only optional properties, then requestBody.required is omitted.
Example:
import { fastifySwagger } from "@fastify/swagger";
import { JsonSchemaToTsProvider } from "@fastify/type-provider-json-schema-to-ts";
import { fastify } from "fastify";
const server = fastify().withTypeProvider<JsonSchemaToTsProvider>();
(async function () {
await server.register(fastifySwagger, {
openapi: {
openapi: "3.1.0",
},
});
server.put(
"/",
{
schema: {
body: {
type: "object",
properties: {
hello: { type: "string" },
},
},
},
},
async (req) => {
console.log(req.body.hello?.length); // hello is optional but the body is not.
},
);
await server.ready();
const swagger = server.swagger();
})();Expected OpenAPI output:
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"hello": { "type": "string" }
}
}
}
}
}Actual OpenAPI output:
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"hello": { "type": "string" }
}
}
}
}
}In Fastify, the body itself is required if schema.body is defined.
The generated OpenAPI spec incorrectly marks it as optional, which can mislead client code generators and API consumers.
Expected Behavior
If a route has schema.body, requestBody.required should always be set to true, regardless of whether any properties inside the body are required.
Current fix using transformObject
transformObject: (documentObject) => {
assert("openapiObject" in documentObject);
const { openapiObject } = documentObject;
for (const pathKey in openapiObject.paths) {
const pathItem = openapiObject.paths[pathKey];
if (!pathItem) continue;
for (const methodKey of Object.keys(pathItem) as Array<keyof typeof pathItem>) {
const operation = pathItem[methodKey];
if (!operation || typeof operation === "string") continue;
if ("requestBody" in operation && operation.requestBody && "content" in operation.requestBody) {
operation.requestBody.required = true;
}
}
}
return openapiObject;
},