- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 167
RFC: Add Model Schemas to OpenApi Docs #157
Comments
Hi @chuckstock. I have added this feature as a "Maybe" in our Initial thoughts 👇 // models.ts
export const User = z.object({
id: z.string().uuid(),
name: z.string(),
});
// router.ts
import { User } from './models';
export const appRouter = trpc.router().query('getUsers', {
meta: { openapi: { ... } },
input: ...,
output: z.array(User),
resolve: ...
});
// openapi.json.ts
import { User } from './models';
import { appRouter } from './router';
const openapi = generateOpenApiDocument(appRouter, {
...,
models: [User]
}); cc @StefanTerdell. |
Yeah this is almost exactly what I was thinking and was potentially expecting to find when reviewing the documentation. I think that makes a lot of sense without adding really any extra boilerplate or overhead. Another package allows you to extend import { extendApi, generateSchema } from '@anatine/zod-openapi';
const aZodExtendedSchema = extendApi(
z.object({
uid: extendApi(z.string().nonempty(), {
title: 'Unique ID',
description: 'A UUID generated by the server',
}),
firstName: z.string().min(2),
lastName: z.string().optional(),
email: z.string().email(),
phoneNumber: extendApi(z.string().min(10), {
description: 'US Phone numbers only',
example: '555-555-5555',
}),
}),
{
title: 'User',
description: 'A user schema',
}
);
const myOpenApiSchema = generateSchema(aZodExtendedSchema); This is another suggestion, although I think I prefer your original suggestion for the API. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Hi, any updates on this? |
I am also very interested in this, its the only open issue on the previous roadmap. Can this still be looked at for a future release? |
From PR #417 : I needed a way to integrate my OpenAPI-extended Zod schemas (using This implementation integrates a registry of Zod schemas into the final document, and also links return, parameter, and request body types of tRPC procedures with these schemas. A minimal example of how this looks with
|
I'm a bit late on the ball here haha. GHs UI sure is one of the UIs of all time Dont know much about open api but I believe it moves the reused schemas out of what would be the idiomatic JSON schema position ("#/definitions" or "#/$defs"). But that's what the definitionPath option is for. It still presumes the root path to be the schema root though, but you can get around that with basePath. Here's a basic example: const baz = z.object({})
zodToJsonSchema(z.object({ x: baz }), {
basePath: ["foo"],
definitionPath: "bar",
definitions: { baz },
}) > {
type: 'object',
properties: { x: { '$ref': 'foo/bar/baz' } },
required: [ 'x' ],
additionalProperties: false,
bar: {
baz: { type: 'object', properties: {}, additionalProperties: false }
}
} You should be able to just move out the definitions object ("bar" in the example) from the resulting schema to put wherever you want in the open api doc as long as you can know the path beforehand and point at it with definitionsPath |
Actually here's a very rough proof of concept @jlalmes import { zodToJsonSchema } from "zodToJsonSchema";
import { ZodSchema, z } from "zod";
function buildDefinition(
paths: Record<
string,
Record<
"get" | "post",
{ summary: string; responses: Record<number, ZodSchema<any>> }
>
>,
schemas: Record<string, ZodSchema<any>>,
) {
const result: any = {
openapi: "3.0.0",
paths: {},
components: {
schemas: {},
},
};
for (const path in paths) {
result.paths[path] ??= {};
for (const method in paths[path]) {
result.paths[path][method] ??= { summary: paths[path][method].summary };
for (const response in paths[path][method].responses) {
result.paths[path][method].responses ??= {};
const zodSchema = paths[path][method].responses[response];
const jsonSchema = zodToJsonSchema(zodSchema, {
target: "openApi3",
basePath: ["#", "components"],
definitionPath: "schemas",
definitions: schemas,
});
result.components.schemas = {
...result.components.schemas,
...(jsonSchema as any).schemas,
};
delete (jsonSchema as any).schemas;
result.paths[path][method].responses[response] = jsonSchema;
}
}
}
return result;
}
// Component to be reused
const todoSchema = z.object({
id: z.string().uuid(),
content: z.string(),
completed: z.boolean().optional(),
});
// The final openapi.json
const result = buildDefinition(
{
todos: {
get: { summary: "Get a todo", responses: { 200: todoSchema } },
post: { summary: "Post a todo", responses: { 201: todoSchema } },
},
},
{ todo: todoSchema },
);
console.dir(result, { depth: null });
const expectResult = {
openapi: "6.6.6",
paths: {
todos: {
get: { $ref: "#/components/schemas/todo" },
post: { $ref: "#/components/schemas/todo" },
},
},
components: {
schemas: {
todo: {
type: "object",
properties: {
id: { type: "string", format: "uuid" },
content: { type: "string" },
completed: { type: "boolean" },
},
required: ["id", "content"],
additionalProperties: false,
},
},
},
}; |
I needed support for this also, published a quick implementation: https://www.npmjs.com/package/@devidw/trpc-openapi Source-Code: https://github.com/devidw/trpc-openapi All you need to do is pass a object with your model schemas when you generate the document: import { generateOpenApiDocument } from "trpc-openapi";
import { z } from "zod";
const SomeSchema = z.object({
a: z.literal(69),
});
generateOpenApiDocument(appRouter, {
// ...
defs: {
SomeSchema,
},
}); |
First, I'm loving the project so far and I really appreciate all the work you've done already.
I honestly don't mind the output requirement on a query/mutation, however, it would be nice to be able to define Model Schema's and reference those for the outputs inside the docs. (ex. Pet Model from Redoc).
The text was updated successfully, but these errors were encountered: