-
-
Notifications
You must be signed in to change notification settings - Fork 404
Open
Description
Description
t.Nullable() produces an OpenAPI schema that combines both nullable: true AND anyOf with { type: "null" }, which is redundant and non-compliant with the OpenAPI specification. This causes issues with SDK generators.
Current Behavior
t.Nullable(t.String({ format: "email" }))Produces:
{
"nullable": true,
"anyOf": [
{ "type": "string", "format": "email" },
{ "type": "null" }
]
}Expected Behavior
For OpenAPI 3.0, the correct schema should be:
{
"type": "string",
"format": "email",
"nullable": true
}Why This Matters
According to the OpenAPI specification:
- OpenAPI 3.0: Does not support
type: "null"directly. You should only usenullable: trueas a modifier on the schema. - OpenAPI 3.1: Removed
nullableentirely in favor of type arrays (type: ["string", "null"]) oranyOf.
The current output is double-specifying nullability, which:
- Is non-compliant with OpenAPI 3.0 spec
- Confuses SDK generators like
@hey-api/openapi-ts(see hey-api/openapi-ts#1160) - Can cause incorrect type generation in client SDKs
Related
- Issue Nullable field for types as documented in OpenAPI 3.0 #669 was closed with a fix, but the issue persists in
[email protected]and@elysiajs/[email protected]
Workaround
We're currently post-processing the OpenAPI spec to fix the schema:
function transformNullableSchemas(spec: OpenAPIV3.Document): OpenAPIV3.Document {
const transform = (obj: unknown): unknown => {
if (obj === null || typeof obj !== "object") return obj;
if (Array.isArray(obj)) return obj.map(transform);
const record = obj as Record<string, unknown>;
if (
record.nullable === true &&
Array.isArray(record.anyOf) &&
record.anyOf.length === 2
) {
const anyOf = record.anyOf as Array<Record<string, unknown>>;
const nullType = anyOf.find((item) => item.type === "null");
const otherType = anyOf.find((item) => item.type !== "null");
if (nullType && otherType) {
const { anyOf: _, ...rest } = record;
return transform({ ...otherType, ...rest, nullable: true });
}
}
const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(record)) {
result[key] = transform(value);
}
return result;
};
return transform(spec) as OpenAPIV3.Document;
}Environment
- Elysia: 1.4.16
- @elysiajs/openapi: 1.4.11
- Bun: 1.3.2
Metadata
Metadata
Assignees
Labels
No labels