Cannot use imported v4 schema in createRoute
Which middleware has the bug?
@hono/zod-openapi
What version of the middleware?
1.1.0
What version of Hono are you using?
4.9.2
What runtime/platform is your app running on? (with version if possible)
Cloudflare Workers
What steps can reproduce the bug?
Consider a following schema to be imported from a library:
export const paginationSchema = new z.$ZodObject({
type: 'object',
shape: {
page: new z.$ZodDefault({
type: 'default',
innerType: new z.$ZodNumber({
type: 'number',
}),
defaultValue: () => 1,
}),
limit: new z.$ZodDefault({
type: 'default',
innerType: new z.$ZodNumber({
type: 'number',
}),
defaultValue: () => 20,
}),
},
})
The library defined the schema using zod/v4/core, following the guidance in Zod docs. However, these core schemas don't have parsing methods like .parse() and .safeParse(). This raises a problem when trying to use the schema directly in createRoute function as it expects full Zod schema with all the parsing methods.
What is the expected behavior?
Basically, I'm stuck between two incompatible approaches: the library correctly follows Zod v4 patterns, but Hono's type system can't work with those patterns. I either have to recreate the entire schema myself, which defeats the purpose of using the library, or I lose all type safety in my route handlers. It seems like there should be a way to bridge these two approaches without sacrificing either reusability or type safety.
What do you see instead?
I tried creating a wrapper function that adds the parsing methods to the core schema, which lets me create the route successfully:
function withMethods<T extends zCore.$ZodObject>(coreSchema: T) {
const methods = {
parse: (data: unknown) => zCore.parse(coreSchema, data),
safeParse: (data: unknown) => zCore.safeParse(coreSchema, data),
parseAsync: (data: unknown) => zCore.parseAsync(coreSchema, data),
safeParseAsync: (data: unknown) => zCore.safeParseAsync(coreSchema, data),
}
return Object.assign(Object.create(Object.getPrototypeOf(coreSchema)), {
...coreSchema,
...methods,
_output: undefined as zCore.infer<T>,
})
}
However, when I try to use the route handler with proper types, the inference completely breaks down as RouteHandler type can't figure out what the query parameters should be:
const { limit } = context.req.valid('query') // Argument of type '"query"' is not assignable to parameter of type 'never'. (ts 2345)
Additional information
No response
Hi @kotkoroid
Where do you import z from? @hono/zod-openapi?
Hi, in the Hono application I use following imports:
import type { RouteHandler } from '@hono/zod-openapi'
import { createRoute, z } from '@hono/zod-openapi'
In the library the schema (paginationSchema) is defined with this import: import * as z from 'zod/v4/core'
@kotkoroid
I think you have to use z from @hono/zod-openapi.
You mean in the library? That goes against the Zod guidelines: https://zod.dev/library-authors?id=how-to-support-zod-4
@kotkoroid
I don't know if it should be called library, but paginationSchema should be defined with z imported from @hono/zod-openapi, not zod.
If you think this is caused by the version difference between v3 and v4 of Zod, can you try it with v3 and confirm that it does not have a problem?
we have a package called @acme/schemas in our monorepo, where other apps import common zod schemas from. we have different applications which use frameworks other than hono. therefore we cannot enforce the usage of import {z} from @hono/zod-openapi in the entire project. all we can do is enforce the same zod version across the monorepo.
it would be nice if @hono/zod-openapi could import regular schemas created by regular zod
@utkuturunc have you found a workaround as we're running into the exact same issue. Monorepo with a package containing schemas that are published and can be used in non-hono projects. So we don't want to depend on @hono/zod-openapi in them.
Using the extendZodWithOpenApi() function also doesn't resolve the issue for me..
@b-vb i mostly ended up rewriting schemas any because i had a special use case that allowed for it. in 2 cases, i was able to use the .openapi method on schemas imported from other packages where they were defined with regular zod. it worked out, i haven't checked why, just taking the win.
i noticed this in the typings:
declare module 'zod' {
interface ZodType<out Output = unknown, out Input = unknown, out Internals extends core.$ZodTypeInternals<Output, Input> = core.$ZodTypeInternals<Output, Input>> extends core.$ZodType<Output, Input, Internals> {
openapi(metadata: Partial<ZodOpenAPIMetadata<Input>>, options?: OpenApiOptions): this;
openapi(refId: string, metadata?: Partial<ZodOpenAPIMetadata<Input>>, options?: OpenApiOptions): this;
}
}
so when i import schemas from other packages into files that have an import to this library, they also end up with the openapi method, and js behind the ts seems to support it as well.
I manage to make it work with extendZodWithOpenApi https://github.com/asteasolutions/zod-to-openapi/blob/master/src/zod-extensions.ts
just put
import z from 'zod';
extendZodWithOpenApi(z);
in any file where your packages is, I wonder if this is more of a problem of zod-openapi or zod-to-openapi seeing how hono rely heavily on zod-to-openapi library