Open Shyrogan opened 4 months ago
Any update so far? I'm facing the same issue
Facing the same issue, did you find a workaround to support recursive types?
Facing the same issue, did you find a workaround to support recursive types?
Sadly there is no other way than lazy
I ended up overwriting type with the .openapi
method like this:
BaseSearchOrdersFiltersSchema.extend({
oneOf: z
.lazy(() => SearchOrdersFiltersSchema.array().optional())
.openapi({
type: "array",
items: {
type: "object",
},
}),
I'm sure there is a better way to define the openapi schema but this at least does not break the spec generation
I found the solution. You can use https://github.com/StefanTerdell/zod-to-json-schema Here is full example:
import { serve } from "@hono/node-server";
import { swaggerUI } from "@hono/swagger-ui";
import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { z } from "@hono/zod-openapi";
import { zodToJsonSchema } from "zod-to-json-schema";
const baseCategorySchema = z.object({ name: z.string() });
type Category = z.infer<typeof baseCategorySchema> & {
subcategories: Category[];
};
const categorySchemaRaw: z.ZodType<Category> = baseCategorySchema.extend({
subcategories: z.lazy(() => categorySchemaRaw.array()),
});
const name = "Category";
const jsonSchema = zodToJsonSchema(categorySchemaRaw, {
basePath: [`#/components/schemas/${name}`],
});
// console.dir(jsonSchema, { depth: null });
const schema = categorySchemaRaw.openapi(name, jsonSchema as {}).openapi({
example: {
name: "test1",
subcategories: [
{
name: "test2",
subcategories: [],
},
],
},
});
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
method: "post",
path: "/test",
request: { body: { content: { "application/json": { schema } } } },
responses: { 200: { description: "test" } },
}),
(c) => c.text("test"),
);
type OpenAPIObjectConfig = Parameters<typeof app.getOpenAPIDocument>[0];
const config: OpenAPIObjectConfig = {
openapi: "3.0.3",
info: { version: "0.0.1", title: "Some API" },
};
const pathOpenAPI = "/openapi";
app.doc(pathOpenAPI, config);
app.get("/swagger-ui", swaggerUI({ url: pathOpenAPI }));
serve({
async fetch(req, env) {
return app.fetch(req, env);
},
port: 4001,
});
// eslint-disable-next-line no-console
console.log(`Server is running on port 4001`);
// const schemaOpenAPI = app.getOpenAPIDocument(config);
// console.dir(schemaOpenAPI, { depth: null });
In swagger UI Category is correctly shown recursively with correct types instead of just any
object.
Also this approach is very convenient to use when you have all your zodiac schemas in one file with a script that converts each zod schema to use openapi with zod-to-json-schema
.
In my case I generate zod types from prisma schema using zod-prisma-types
and since there are a lot of usage of zod.Lazy so I then run my own script to convert each zod schema to be as in example above(use json schema for openapi).
The significant benefit of this approach is that you extract your reusable openapi schemas into #/components/schemas/
and it is displayed below in swagger ui editor.
I found the solution. You can use https://github.com/StefanTerdell/zod-to-json-schema Here is full example:
import { serve } from "@hono/node-server"; import { swaggerUI } from "@hono/swagger-ui"; import { createRoute, OpenAPIHono } from "@hono/zod-openapi"; import { z } from "@hono/zod-openapi"; import { zodToJsonSchema } from "zod-to-json-schema"; const baseCategorySchema = z.object({ name: z.string() }); type Category = z.infer<typeof baseCategorySchema> & { subcategories: Category[]; }; const categorySchemaRaw: z.ZodType<Category> = baseCategorySchema.extend({ subcategories: z.lazy(() => categorySchemaRaw.array()), }); const name = "Category"; const jsonSchema = zodToJsonSchema(categorySchemaRaw, { basePath: [`#/components/schemas/${name}`], }); // console.dir(jsonSchema, { depth: null }); const schema = categorySchemaRaw.openapi(name, jsonSchema as {}).openapi({ example: { name: "test1", subcategories: [ { name: "test2", subcategories: [], }, ], }, }); const app = new OpenAPIHono({ strict: false }); app.openapi( createRoute({ method: "post", path: "/test", request: { body: { content: { "application/json": { schema } } } }, responses: { 200: { description: "test" } }, }), (c) => c.text("test"), ); type OpenAPIObjectConfig = Parameters<typeof app.getOpenAPIDocument>[0]; const config: OpenAPIObjectConfig = { openapi: "3.0.3", info: { version: "0.0.1", title: "Some API" }, }; const pathOpenAPI = "/openapi"; app.doc(pathOpenAPI, config); app.get("/swagger-ui", swaggerUI({ url: pathOpenAPI })); serve({ async fetch(req, env) { return app.fetch(req, env); }, port: 4001, }); // eslint-disable-next-line no-console console.log(`Server is running on port 4001`); // const schemaOpenAPI = app.getOpenAPIDocument(config); // console.dir(schemaOpenAPI, { depth: null });
In swagger UI Category is correctly shown recursively with correct types instead of just
any
object.Also this approach is very convenient to use when you have all your zodiac schemas in one file with a script that converts each zod schema to use openapi with
zod-to-json-schema
.In my case I generate zod types from prisma schema using
zod-prisma-types
and since there are a lot of usage of zod.Lazy so I then run my own script to convert each zod schema to be as in example above(use json schema for openapi). The significant benefit of this approach is that you extract your reusable openapi schemas into#/components/schemas/
and it is displayed below in swagger ui editor.
Thank you for this, will look into it :)
I still think it could be a great improvement for Hono !
Similarly to how
zod-openapi
does it: https://github.com/samchungy/zod-openapi/tree/master?tab=readme-ov-file#supported-zod-schemaCurrently, we get the following error:
A good example of usage would be the JSON schema from Zod's documentation: