honojs / middleware Goto Github PK
View Code? Open in Web Editor NEWmonorepo for Hono third-party middleware/helpers/wrappers
Home Page: https://hono.dev
monorepo for Hono third-party middleware/helpers/wrappers
Home Page: https://hono.dev
I am creating a route as following, but the types are weird for c.req.valid('json')
const schema = z.object({
email: z.string().email(),
name: z.string().email(),
password: z.string(),
})
app.post(
'/users/register',
zValidator('json', schema),
(c) => {
const data = c.req.valid('json');
return c.json({
ok: true
});
},
);
The above screenshot shows the types returned by c.req.valid('json')
is
in: {
json: { ... }
},
out: {
json: { ... }
}
When I use zValidator for validation I encounter a typescript bug.
When I coerce something inside the validator the resulting type is always never.
z.object({quality: z.coerce.number().min(1).max(100)})
So the validation works and the incoming c.req.valid is also correct. It is just the typescript definition that breaks.
The toucan-js
version in the sentry middleware is at 2.x
, while the latest is 3.x
. Specifically, the setContext
API doesn't seem to be available, which is really useful for adding additional details to errors.
Moin!
First of all, thank you so much for developing Hono! I love it and can't ever go back to other frameworks.
In the past, I have included the Server-Timing API everywhere I could. It helps me a lot, especially in production systems, to be able to keep track of individual durations.
To be able to use this functionality in hono, I wrote the middleware and functions for it. Now I wanted to ask if it is wanted to add this to the third-party or even to core.
The usage looks something like this:
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { endTime, setMetric, startTime, timing } from "@puazzi/hono-timing";
const app = new Hono();
// add the middleware to your router
app.use('*', timing());
app.get('/', async (c) => {
// add custom metrics
setMetric(c, 'region', 'europe-west3')
// add custom metrics with timing, must be in milliseconds
setMetric(c, 'custom', 23.8, 'My custom Metric')
// start a new timer
startTime(c, 'db');
const data = await db.findMany(...);
// end the timer
endTime(c, 'db');
return c.json({ response: data });
});
serve(app);
Best regards from Germany!
Thanks to Cloudflare and Hono It's a fun library ๐ฏ
By the way conflicts occur when doing npm i
locally.
Do I need to upgrade the version of hono that @hono/firebase-auth depends on?
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: @hono/[email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/hono
npm ERR! hono@"^3.0.2" from the root project
npm ERR! peer hono@"^3.0.2" from @hono/[email protected]
npm ERR! node_modules/@hono/sentry
npm ERR! @hono/sentry@"^0.2.0" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/hono
npm ERR! peer hono@"^2.7.2" from @hono/[email protected]
npm ERR! node_modules/@hono/firebase-auth
npm ERR! @hono/firebase-auth@"^1.1.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
"dependencies": {
"@hono/sentry": "^0.2.0",
"@hono/firebase-auth": "^1.1.0",
"hono": "^3.0.2"
}
My environment:
Node : 19.7.0
Mac OS 13.0.1
Thank you for your help in advance.
This is just a hint that we will change the safeParse
API. .error
will be deprecated and .issues
will be added.
How does one access bindings values (for API keys) when using trpcServer
middleware?
Without Hono, one accesses env values (or bindings) from the env param of the fetch
handler.
(has the signature async fetch(request: Request, env, ctx)
)
With Hono, normally you would just call c.env.SOME_KEY
inside a route.
But I need to init my trpc API with keys for my backend services.
So how do I access the binding inside the createContext
callback?
type Bindings = {
SOME_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use(
"/trpc/*",
trpcServer({
router: appRouter,
onError({ error }) {
console.error(error);
},
createContext: (opts) =>
createContext({
...opts,
SOME_KEY: c.env.SOME_KEY, // how to do this?
}),
}),
);
Hello there!
I recently began experimenting with @hono/zod-validator
. Unfortunately, I encountered an error message stating Malformed JSON in request body
in request body. I was wondering if anyone has come across this issue before and if there's anyone who could offer assistance in finding a solution.
My client sends a request:
import type { AppType } from '../server/router'
import { hc } from 'hono/client'
const client = hc<AppType>('https://localhost/');
const res = await client.testrpc.$post({
json: {
x: 'Hello',
},
});
And the server receives it:
const route = app.post(
'/testrpc',
zValidator(
'json',
z.object({
x: z.string(),
})
),
(c) => {
const data = c.req.valid('json');
return c.jsonT({
success: true,
message: `${data.x}`,
}, 200);
}
)
Thank you in advance for your help!
I get the following warning on the basic implementation. Not sure how to resolve it.
Your worker called response.clone(), but did not read the body of both clones. This is wasteful, as it forces the system to buffer the entire response body in memory, rather than streaming it through. This may cause your worker to be unexpectedly terminated for going over the memory limit. If you only meant to copy the response headers and metadata (e.g. in order to be able to modify them), use
new Response(response.body, response)
instead.
app.post("/", zValidator("json", schema), async (c) => {
const data = c.req.valid("json");
return c.json({ data });
});
Currently in @hono/firebase-auth, projectId is passed to the middleware setter via a config object. Other values are passed via environment and internally set in the MiddlewareHandler. I'm not sure how/where I'd be able to wrap/inject the PROJECT_ID environment variable to the config object and the app.use("*", verifyFirebaseAuth(config));
middleware setter.
Isn't this required to support multiple Firebase projects (1 project_id per environment like staging, prod)?
Internally, would most of it be handled if the above line was changed to: config.projectId ?? c.env.PROJECT_ID
?
I'd love to open a PR if this makes sense, or would appreciate guidance around proper usage if it's currently supported a different way.
Hi,
I've been playing with Hono (with Node) for a few days at work and we've seen some good performance from it.
After testing zod-to-openapi
I realized that we could add an additional route with the extension .yml
to output the specs as YAML when needed.
e.g.: The example below generates both /docs
and /docs.yml
routes
const app = new OpenAPIHono()
app.doc('/docs', swaggerOptions)
Reason
The scenario would be using it with an Online Swagger Editor or simply connecting to your editor of choice.
For performance reasons, it only parses it when the route is triggered:
Example
handler.openapi(routeSchama, async (c) => {
const YAML = await import('js-yaml');
const result = YAML.dump(json)
return c.text(result)
})
Also, YAML parsing could be a new Hono helper such as hono/html
if it becomes useful for more devs.
Hi team,
I encountered this issue when trying to build an API with more than one input type (e.g. PATH + QUERY like /{path}?q=xxx
).
When using zod-openapi
with a route including query
input data with (or without) params
, the type resolution fails when trying to access the input from the handler.
In this context (the sample file is provided at the end of this post)
app.openapi(route, async (c) => {
const { paramValue } = c.req.valid('param') as ParamSchema;
const { queryValue } = c.req.valid('query') as QuerySchema;
...
});
the calls to c.req.valid
will produce the following errors at compile time:
src/min.ts:43:40 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
43 const { paramValue } = c.req.valid('param') as ParamSchema;
~~~~~~~
src/min.ts:44:40 - error [tsc.trace.both.gz](https://github.com/honojs/middleware/files/12908484/tsc.trace.both.gz): Argument of type 'string' is not assignable to parameter of type 'never'.
44 const { queryValue } = c.req.valid('query') as QuerySchema;
~~~~~~~
Found 2 errors in the same file, starting at: src/min.ts:43
that is the output of bun run tsc --noEmit --pretty --skipLibCheck --strict src/min.ts
Note that the problem disappears if we just use the param
as input:
const route = createRoute({
method: 'get',
path: '/{paramValue}/path',
request: {
params: ParamSchema,
// query: QuerySchema,
},
...
});
app.openapi(route, async (c) => {
const { paramValue } = c.req.valid('param') as ParamSchema; // Works !
...
});
But not when commenting out the param
and leaving just the query:
const route = createRoute({
method: 'get',
path: '/path', // Note the {param} is removed from the path
request: {
//params: ParamSchema,
query: QuerySchema,
},
...
});
const app = new OpenAPIHono();
app.openapi(route, async (c) => {
// error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
const { queryValue } = c.req.valid('query') as QuerySchema;
...
});
Looking at the build traces from tsc
, we can see for param
the display type is correct <T extends \"param\">(target: T) ...
allowing to proceed without any errors.
Param only
{"id":15461,"symbolName":"valid","recursionId":3796,"firstDeclaration":{"path":"/home/user/Documents/substreams-clock-api/node_modules/hono/dist/types/request.d.ts","start":{"line":57,"character":71},"end":{"line":58,"character":94}},"flags":["Object"],"display":"<T extends \"param\">(target: T) => InputToDataByTarget<{ param: { paramValue: \"a\" | \"b\" | \"c\"; }; }, T>"},
However in the other cases, it resolves to <T extends never>(target: T) ...
making it impossible to infer the type.
Query only
{"id":15453,"symbolName":"valid","recursionId":3793,"firstDeclaration":{"path":"/home/user/Documents/substreams-clock-api/node_modules/hono/dist/types/request.d.ts","start":{"line":57,"character":71},"end":{"line":58,"character":94}},"flags":["Object"],"display":"<T extends never>(target: T) => InputToDataByTarget<undefined, T> | InputToDataByTarget<Partial<{ json: unknown; form: unknown; query: unknown; queries: unknown; param: unknown; header: unknown; cookie: unknown; }>, T>"},
Both
{"id":15472,"symbolName":"valid","recursionId":3803,"firstDeclaration":{"path":"/home/user/Documents/substreams-clock-api/node_modules/hono/dist/types/request.d.ts","start":{"line":57,"character":71},"end":{"line":58,"character":94}},"flags":["Object"],"display":"<T extends never>(target: T) => InputToDataByTarget<undefined, T> | InputToDataByTarget<Partial<{ json: unknown; form: unknown; query: unknown; queries: unknown; param: unknown; header: unknown; cookie: unknown; }>, T>"},
The full traces for each are available here: tsc.traces.tar.gz
In #77, the problem appeared to be fixed by using strict: true
for the config and upgrading to the latest versions of hono
and @hono/zod-validator
. This doesn't seem to fix the problem in this case.
Bun version 1.0.2
bun pm ls
node_modules (16)
โโโ @hono/[email protected]
โโโ @sinclair/[email protected]
โโโ [email protected]
โโโ [email protected]
โโโ [email protected]
โโโ [email protected]
โโโ [email protected]
import { OpenAPIHono, z, createRoute } from '@hono/zod-openapi';
import { TypedResponse } from 'hono';
const ParamSchema = z.object({
paramValue: z.enum(['a', 'b', 'c'])
.openapi({
param: {
name: 'paramValue',
in: 'path',
}
})
});
type ParamSchema = z.infer<typeof ParamSchema>;
const QuerySchema = z.object({
queryValue: z.coerce.number()
.openapi({
param: {
name: 'queryValue',
in: 'query',
}
})
});
type QuerySchema = z.infer<typeof QuerySchema>;
const route = createRoute({
method: 'get',
path: '/{paramValue}/path',
request: {
params: ParamSchema,
query: QuerySchema,
},
responses: {
200: {
description: 'Sample endpoint',
},
},
});
const app = new OpenAPIHono();
app.openapi(route, async (c) => {
const { paramValue } = c.req.valid('param') as ParamSchema;
const { queryValue } = c.req.valid('query') as QuerySchema;
return {
response: c.text("Not working...")
} as TypedResponse<string>;
});
export default app;
Following the README section of middleware - https://github.com/honojs/middleware/tree/main/packages/zod-openapi#middleware
My understanding is that Zod OpenAPI hono is simply an extension on top of the regular Hono, and so any Hono work should be compatible with OpenAPI Hono. As such I would expect the following to work using OpenAPIHono()
(using the Hono JWT for example)-
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
const app = new OpenAPIHono();
app.use("/fizz", jwt({ secret: "a-secret" }));
app.get("/fizz", (c) => {
const payload = c.get("jwtPayload");
return c.json(payload);
});
However, TypeScript gives me a large type error on the app.use()
line -
No overload matches this call.
Overload 1 of 2, '(...handlers: MiddlewareHandler<Env, never, {}>[]): Hono<Env, {}, "/">', gave the following error.
Argument of type 'string' is not assignable to parameter of type 'MiddlewareHandler<Env, never, {}>'.
Overload 2 of 2, '(path: "/fizz", ...handlers: MiddlewareHandler<Env, "/fizz", {}>[]): Hono<Env, {}, "/">', gave the following error.
Argument of type 'MiddlewareHandler' is not assignable to parameter of type 'MiddlewareHandler<Env, "/fizz", {}>'.
Types of parameters 'c' and 'c' are incompatible.
Type 'Context<Env, "/fizz", {}>' is not assignable to type 'Context<any, string, {}>'.
Types of property 'req' are incompatible.
Type 'HonoRequest<"/fizz", unknown>' is not assignable to type 'HonoRequest<string, unknown>'.
Types have separate declarations of a private property 'paramData'.ts(2769)
I am finding this error on any use of app.use()
, not just for the JWT.
If it is relevant, I am using bun
Hi! I was planning on making my own framework as I found most frameworks having issues with edge environments & this framework is awesome so far! So first of all great work!!
As I started looking at the packages, I realized that denoify was being used to convert the package from an npm module to a deno module. Deno has an official package that does the opposite & seems to have better support, you can find it here.
I was wondering if it would make sense to reverse it to go from Deno to NPM instead of NPM to Deno?
This is ported from https://github.com/honojs/graphql-server/issues/19
Hi, I am currently using hono with nodejs 18. When I create a Client for example
export const Client = hc<typeof myRouter>(
"http://localhost:8999/api",
{
headers: {
"X-User-Agent": "hc",
"Accept-Encoding": "gzip, deflate, br",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "",
},
},
);
And a route uses the zValidator Middleware. I get type errors when I want to add headers to the Request, for example:
await Client.someData.$get({
query: {
data: data,
},
headers: {
Authorization: `Bearer JWT`,
},
});
I get TS2345: Argument of type '{ query: { data: data; }; headers: { Authorization: string; }; }' is not assignable to parameter of type '{ query: { data: data; }; }'.
Object literal may only specify known properties, and 'headers' does not exist in type '{ query: { data: data; }; }'.
TRPC routes are there but still getting 404
TURBOREPO
src/trpc/init.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
import { ZodError } from 'zod';
const t = initTRPC.create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
export const publicProcedure = t.procedure;
export const TRPCRouter = t.router;
src/trpc/index.ts
import { z } from 'zod';
import { publicProcedure, TRPCRouter } from './init';
export const appRouter = TRPCRouter({
hello: publicProcedure.input(z.string().nullish()).query(({ input }) => {
return `Hello ${input ?? 'World'}!`;
}),
got: publicProcedure.query(() => {
return `Hello 'World'!`;
}),
nest: TRPCRouter({
hello: publicProcedure.input(z.string()).query(({ input }) => {
return `Hello ${input}!`;
}),
}),
});
export type AppRouter = typeof appRouter;
src/index.ts
import { Hono } from 'hono';
import { trpcServer } from '@hono/trpc-server';
import { serve } from '@hono/node-server';
import { renderTrpcPanel } from 'trpc-panel';
import { compress } from 'hono/compress';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { appRouter } from './trpc';
import { env } from './env';
const app = new Hono();
app.use('*', cors());
app.use('*', compress());
app.use('*', logger());
console.log('Starting server...');
console.log({
env,
});
app.get('/panel', (c) => {
return c.html(
renderTrpcPanel(appRouter, {
url: 'http://localhost:9879/back/trpc',
transformer: 'superjson',
})
);
});
app.use(
'/back/trpc/*',
trpcServer({
router: appRouter,
})
);
serve({
fetch: app.fetch,
port: 9879,
});
{
"name": "goat",
"version": "0.0.0",
"license": "UNLICENSED",
"private": true,
"scripts": {
"start": "tsx src/index.ts",
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@hono/node-server": "^1.1.1",
"@hono/trpc-server": "^0.1.0",
"@prisma/client": "^5.3.1",
"@trpc/server": "^10.38.3",
"dotenv": "^16.3.1",
"envalid": "^7.3.1",
"hono": "^3.6.0",
"next-auth": "^4.23.1",
"superjson": "^1.13.1",
"trpc-panel": "^1.3.4",
"zod": "^3.22.2"
},
"devDependencies": {
"@types/node": "^20.6.2",
"prisma": "^5.3.1",
"tsx": "^3.12.2",
"typescript": "^5.2.2"
}
}
Thank you!
Hello! I'm running into this error with wrangler
service core:user:binto: Uncaught TypeError: Cannot read properties of undefined (reading 'query')
at index.js:2825:22
โ [ERROR] MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above.
when trying to isolate my trpc routes into separate routers each defined in their own file. I have gotten trpc to work successfully but only when my routes are defined all in a single router in the same routes/index.ts file. It would seem like it can't import the routers properly as it says it cannot read query of undefined.
"wrangler": "^3.12.0"
"@hono/trpc-server": "^0.1.0",
"@trpc/server": "^10.40.0",
"hono": "^3.8.1",
index.ts
import { Hono } from 'hono'
import { trpcServer } from '@hono/trpc-server'
import { initTRPC } from '@trpc/server';
import { appRouter } from './routers';
const app = new Hono()
const t = initTRPC.create();
export const router = t.router;
export const middleware = t.middleware;
export const publicProcedure = t.procedure;
app.use(
'/trpc/*',
trpcServer({
router: appRouter,
})
)
export default app
routers/index.ts
import { userRouter } from './user';
import { router } from '..';
export const appRouter = router({
user: userRouter
});
export type AppRouter = typeof appRouter;
routes/user.ts
import { publicProcedure, router } from '..';
export const userRouter = router({
hello: publicProcedure.query(() => {
return [];
}),
});
Hi,
Hono is awesome but is it possible to add swagger middleware ?
Best regards
Vincent
Hello ๐, I hope you're doing well! I've encountered an issue with a type mismatch when trying to use the zValidator middleware from the @hono/zod-validator package with Hono. The error occurs when passing the middleware to app.post().
First, I'd like to thank you for creating such a great library ๐. Here's the code snippet to reproduce the issue:
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono<{ Bindings: Bindings; Variables: { sentry: Toucan; tenantId: string; } }>();
const postBalanceValidator = zValidator('form', z.object({ amount: z.number() }));
app.post(
'/balance',
postBalanceValidator, // <-- ERROR HERE
(c) => addBalance(c)
);
The TypeScript compiler reports the following error:
ใใฎๅผใณๅบใใซไธ่ดใใใชใผใใผใญใผใใฏใใใพใใใ
ๅๅใฎใชใผใใผใญใผใใซใใใๆฌกใฎใจใฉใผใ็บ็ใใพใใใ
ๅ 'MiddlewareHandler<Env, string, { in: { form: { amount: number; }; }; out: { form: { amount: number; }; }; }>' ใฎๅผๆฐใๅ 'H<{ Bindings: Bindings; Variables: { sentry: Toucan;tenantId: string; } ; }, string, Input, {}>' ใฎใใฉใกใผใฟใผใซๅฒใๅฝใฆใใใจใฏใงใใพใใใ
ๅ 'MiddlewareHandler<Env, string, { in: { form: { amount: number; }; }; out: { form: { amount: number; }; }; }>' ใๅ 'MiddlewareHandler<{ Bindings: Bindings; Variables: { sentry: Toucan;tenantId: string; } ; }, string, Input>' ใซๅฒใๅฝใฆใใใจใฏใงใใพใใใ
ใใฉใกใผใฟใผ 'c' ใใใณ 'c' ใฏๅใซไบๆๆงใใใใพใใใ
ๅ 'Context<{ Bindings: Bindings; Variables: { sentry: Toucan;tenantId: string; } ; }, string, Input>' ใๅ 'Context<Env, string, { in: { form: { amount: number; }; }; out: { form: { amount: number; }; }; }>' ใซๅฒใๅฝใฆใใใจใฏใงใใพใใใ
ๅ 'Env' ใๅ '{ Bindings: Bindings; Variables: { sentry: Toucan;tenantId: string; } ; }' ใซๅฒใๅฝใฆใใใจใฏใงใ
Your help would be greatly appreciated! Thank you in advance ๐.
This is a continuation from honojs/hono#1529 (comment), which I still think is a problem on second thought.
This is the reproduction code I've shown in the issue above
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
const userSchema = z.object({ id: z.string() });
declare const getUserFromDatabase: (
id: string,
) => z.infer<typeof userSchema> | undefined;
export default new OpenAPIHono()
.openapi(
createRoute({
method: "get",
path: "/users/{id}",
responses: {
200: {
content: { "application/json": { schema: userSchema } },
description: "Found",
},
404: { description: "Not found" },
},
}),
(c) => {
const user = getUserFromDatabase(c.req.param("id"));
return user ? c.jsonT(user) : c.jsonT(null, 404);
},
)
.openapi(
createRoute({
method: "put",
path: "/users/{id}",
responses: { 204: { description: "Successfull update" } },
}),
(c) => {
// update user info here.
return c.jsonT(null, 204)
},
);
and c.jsonT(null)
yields the following compile error.
Argument of type '(c: Context<Env, "/users/:id", {}>) => TypedResponse<{ id: string; }> | TypedResponse<null>' is not assignable to parameter of type 'Handler<Env, "/users/:id", {}, HandlerResponse<{} | { id: string; }>>'.
Type 'TypedResponse<{ id: string; }> | TypedResponse<null>' is not assignable to type 'HandlerResponse<{} | { id: string; }>'.
Type 'TypedResponse<null>' is not assignable to type 'HandlerResponse<{} | { id: string; }>'.
Type 'TypedResponse<null>' is not assignable to type 'TypedResponse<{} | { id: string; }>'.
Type 'null' is not assignable to type '{} | { id: string; }'.(2345)
Replacing null
with {}
eliminates the compile error but is problematic in case of 204 status response. You have to use null
as a response body if the status is 204:
import { Hono } from "hono";
// TypeError: Response constructor: Invalid response status code 204
await new Hono().get("/", (c) => c.jsonT({}, 204)).request("/");
Returning either null
or {}
for 4xx/5xx responses might be a personal preference (I'd prefer the former), but I'd be happy if we can return null
in Zod OpenAPI Hono because there are no choices for 204.
So I'm trying to implement @hono/zod-validator in my project. After following the README.md instruction everything works well and req.valid('json');
is correctly typed using the provided zod schema.
But I like to have my handler functions in a separate typescript file. So this is how I have it set up now:
// user.route.ts
app.post('/', zValidator('json', ...zodSchema), userLogInController); // Typescript not complaining
// user.controller.ts
export function userLogInController(c: Context) {
const { email, password } = c.req.valid('json'); // [TS error]: Argument of type 'string' is not assignable to parameter of type 'never'.
return c.text('Test');
}
It would be nice if there was a built in generic type that you can use to extend the Context type or Input type with my zod schema types.
For now I wrote my own generic type for this use case:
// utils/zod.ts
export type ZodContext<
Target extends keyof ValidationTargets,
Schema extends ZodType<any, any>
> = Context<
Env,
string,
{
in: {
[key in Target]: z.infer<Schema>;
};
out: {
[key in Target]: z.infer<Schema>;
};
}
>;
// user.schema.ts
const userLogInZodSchema = z.object({
email: z.string().email(),
password: z.string(),
});
export const userLogInSchema = zValidator('json', userLogInZodSchema);
export type UserLogInContext = ZodContext<'json', typeof userLogInZodSchema>;
// user.route.ts
app.post('/log-in', userLogInSchema, userLogInController);
// user.controller.ts
export async function userLogInController(c: UserLogInContext) {
const { email, password } = c.req.valid('json'); // No type errors anymore!
return c.text('It works!');
}
I think it would be nice if something like this would be built in to @hono/zod-validator.
This will be ok
const app = new OpenAPIHono()
app.openapi(route, (c) => c.jsonT({ message: 'Hello' }))
But this will be error
const app = new OpenAPIHono().basePath("/hoge")
app.openapi(route, (c) => c.jsonT({ message: 'Hello' }))
this won't be type error, but basePath doesn't work
const app = new OpenAPIHono()
app.basePath("/hoge")
app.openapi(route, (c) => c.jsonT({ message: 'Hello' }))
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
const route = createRoute({
method: 'get',
path: '/message',
responses: {
200: {
description: 'Get message',
},
},
})
const app = new OpenAPIHono()
app.basePath('/api')
app.openapi(route, (c) => c.jsonT({ message: 'Hello' }))
export default app;
When registering openapi
routes, there is currently no way to make this work with hc
for more than 1 route.
Repro: https://tsplay.dev/mpQPgW
Taking the example from the README:
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
const app = new Hono();
const schema = z.object({
name: z.string(),
age: z.number(),
});
app.post("/author", zValidator("json", schema), (c) => {
const data = c.req.valid("json");
return c.json({
success: true,
message: `${data.name} is ${data.age}`,
});
});
Gives me this error:
src/example.ts:16:22 - error TS2339: Property 'name' does not exist on type 'never'.
16 message: `${data.name} is ${data.age}`,
~~~~
src/example.ts:16:38 - error TS2339: Property 'age' does not exist on type 'never'.
16 message: `${data.name} is ${data.age}`,
~~~
Not sure what I'm doing wrong, my tsconfig is pretty minimal:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext"],
"types": ["@cloudflare/workers-types/2022-11-30"],
"moduleResolution": "node"
},
"include": ["src/**/*.ts"]
}
Hi everyone,
https://[email protected]/?page=1&limit=15&list=1%2C2%2C3
new URLSearchParams({page: 1, limit: 15, list: [1, 2, 3]}).toString()
import { Hono } from 'hono';
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
const app = new Hono<{ Bindings: { aaa: string } }>().basePath('/api');
const schema = /???/
app.get('/post', zValidator('query', schema), async (c) => {
const query=c.req.query('query')
return c.json({ docs:[]});
});
i need to know how to write zod schema for this query
First of all, I'm really enjoying using Hono - great job on speed and simplicity ๐. I initially started with plain REST, but then moved on to using GraphQL using the middleware you provide. Pothos is a type safe GraphQL schema builder which I'd really like to use together with Hono, but I'm having some issues trying to get them to work together.
authScopes
for resolvers, the GraphQL processing returns this error: "Invalid value used as weak map key". I raised an issue in the Pothos repo here: hayes/pothos#1064, and the author thinks it's related to propagating the context object, so if 1 is solved, then this should be solved also.I'm using Cloudflare Workers and it's not trivial to get everything up and running, so I have created a full repro of these issues, along with tests that demonstrate them at this repo: https://github.com/armstrong-pv/hono-pothos-issue-repro
There is a readme which covers setting everything up and running the tests: https://github.com/armstrong-pv/hono-pothos-issue-repro/blob/main/README.md.
It's pretty straightforward:
npm install
npm run tests
If you want to run in dev mode:
npm run dev
Many thanks!
It should receive args like a strict: false
.
Hi,
I was trying to make this work with deno runtime, but I guess might be an issue across all edge networks.
We need to set the middleware for all routes. Reason, on my home page if the user is signed in , (already has a valid authorization) then I need to show Logout Button.
// set middleware
app.use('*', verifyFirebaseAuth(config))
Now, let say my root route.
app.get('/', (c) => {
return c.text('Hello from home')
})
because the middle ware is called on each route invocation and we try to
const authorization = c.req.headers.get(config.AuthorizationHeaderKey)
if the authorization is null, we are sending null response with 400. This results in home page also not being shown. So I think, this behavior is not proper or I might be missing something.
Hi, I was wondering if it would be possible to export the registry
on the zod-openapi
middleware, I'd like to create a component like the one below so I can represent that parts of my API require a Bearer token in order to be accessed.
const bearerAuth = registry.registerComponent(
'securitySchemes',
'bearerAuth',
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
}
);
Thanks in advance and I'd like to praise this amazing package, never found such a nice "framework" to work with!
When I try to use the OpenAPIHono app and plug the CORS Middleware :
const app = new OpenAPIHono();
app.use('/api/*', cors());
It gives me an error :
No overload matches this call.
Overload 1 of 2, '(...handlers: MiddlewareHandler<Env, never, {}>[]): Hono<Env, {}, "/">', gave the following error.
Argument of type 'string' is not assignable to parameter of type 'MiddlewareHandler<Env, never, {}>'.
Overload 2 of 2, '(path: "/api/*", ...handlers: MiddlewareHandler<Env, "/api/*", {}>[]): Hono<Env, {}, "/">', gave the following error.
Argument of type 'MiddlewareHandler' is not assignable to parameter of type 'MiddlewareHandler<Env, "/api/*", {}>'.
Types of parameters 'c' and 'c' are incompatible.
Type 'Context<Env, "/api/*", {}>' is not assignable to type 'Context<any, string, {}>'.
Types of property 'req' are incompatible.
Type 'HonoRequest<"/api/*", unknown>' is not assignable to type 'HonoRequest<string, unknown>'.
Types have separate declarations of a private property '_s'
I expect the to be valid as an Array
even if it has only one element like keys=foo
.
Actually, I got an error: path":["keys"],"message":"Expected array, received string"}]
.
/test?keys=foo&keys=bar
/test?keys=foo
How do I pass both of 1 & 2?
Thank you.
import { createRoute, z, OpenAPIHono } from '@hono/zod-openapi';
const app = new OpenAPIHono();
const ParamsSchema = z.object({
keys: z
.string()
.array()
.openapi({
example: ['foo', 'bar'],
}),
});
const route = createRoute({
method: 'get',
path: '/test',
request: {
query: ParamsSchema,
},
responses: {
200: {
content: {
'application/json': {
schema: ParamsSchema,
},
},
},
},
});
Following on from #187, I have a separate issue specifically for managing JWTs in Zod OpenAPI Hono.
Taking the following JWT code in Hono -
import { Hono } from "hono";
import { jwt } from "hono/jwt";
const happ = new Hono();
happ.use("/fizz", jwt({ secret: "a-secret" }));
happ.get("/fizz", (c) => {
const payload = c.get("jwtPayload");
return c.json(payload);
});
export default happ;
The following request succeeds with 200
as it should:
curl --request GET \
--url http://localhost:3000/fizz \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
And this request with an invalid Bearer token gives 401
as it should:
curl --request GET \
--url http://localhost:3000/fizz \
--header 'Authorization: Bearer imbad'
Converting this code into Zod OpenAPI Hono like so -
import { OpenAPIHono } from "@hono/zod-openapi";
import { jwt } from "hono/jwt";
const app = new OpenAPIHono();
app.use("/fizz", jwt({ secret: "a-secret" }));
app.get("/fizz", (c) => {
const payload = c.get("jwtPayload");
return c.json(payload);
});
export default app;
When I run the valid request, I do in fact get the 200
response which is working as expected.
However, when I run an invalid request, instead of the 401
, I receive a 500
internal server error of the following:
<-- GET /fizz
1 | // src/http-exception.ts
2 | var HTTPException = class extends Error {
3 | constructor(status = 500, options) {
4 | super(options?.message);
^
error: Error
at new HTTPException (/Users/callum/Equator/borealis/node_modules/hono/dist/http-exception.js:4:4)
at /Users/callum/Equator/borealis/node_modules/hono/dist/middleware/jwt/index.js:55:12
at processTicksAndRejections (:55:76)
at errorHandler (/Users/callum/Equator/borealis/node_modules/@hono/zod-openapi/node_modules/hono/dist/hono-base.js:19:2)
at /Users/callum/Equator/borealis/node_modules/@hono/zod-openapi/node_modules/hono/dist/compose.js:58:32
at /Users/callum/Equator/borealis/node_modules/@hono/zod-openapi/node_modules/hono/dist/compose.js:55:32
at processTicksAndRejections (:55:76)
--> GET /fizz 500 2ms
So I am left wondering what has happened in Zod OpenAPI Hono that has caused this server error to appear?
If it is relevant, I am using bun
When a binding to an mtls cert is defined in wrangler.toml
mtls_certificates = [
{ binding = "MY_CERT", certificate_id = "xxxxx" },
]
The binding does not appear in context.env. Is this enabled in hono?
the issues seem to be mostly related to Request
, Response
, File
, ReferrerPolicy
, Headers
, RequestInit
, FetchEvent
, FormData
, ReadableStream<Uint8Array>
, RequestMode
, RequestCredentials
, RequestCache
, ...
../../node_modules/@trpc/server/dist/adapters/fetch/fetchRequestHandler.d.ts:4:10 - error TS2304: Cannot find name 'Request'.
4 req: Request;
~~~~~~~
../../node_modules/@trpc/server/dist/adapters/fetch/fetchRequestHandler.d.ts:7:124 - error TS2304: Cannot find name 'Response'.
7 export declare function fetchRequestHandler<TRouter extends AnyRouter>(opts: FetchHandlerRequestOptions<TRouter>): Promise<Response>;
~~~~~~~~
../../node_modules/@trpc/server/dist/adapters/fetch/types.d.ts:4:10 - error TS2304: Cannot find name 'Request'.
4 req: Request;
~~~~~~~
../../node_modules/@trpc/server/dist/adapters/fetch/types.d.ts:5:17 - error TS2304: Cannot find name 'Headers'.
5 resHeaders: Headers;
~~~~~~~
../../node_modules/@trpc/server/dist/adapters/fetch/types.d.ts:19:102 - error TS2304: Cannot find name 'Request'.
19 export declare type FetchHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<TRouter, Request> & FetchCreateContextOption<TRouter>;
~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:17:71 - error TS2304: Cannot find name 'Response'.
17 (data: Data | null, status?: StatusCode, headers?: HeaderRecord): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:18:32 - error TS2304: Cannot find name 'ResponseInit'.
18 (data: Data | null, init?: ResponseInit): Response;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:18:47 - error TS2304: Cannot find name 'Response'.
18 (data: Data | null, init?: ResponseInit): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:23:66 - error TS2304: Cannot find name 'Response'.
23 (text: string, status?: StatusCode, headers?: HeaderRecord): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:24:27 - error TS2304: Cannot find name 'ResponseInit'.
24 (text: string, init?: ResponseInit): Response;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:24:42 - error TS2304: Cannot find name 'Response'.
24 (text: string, init?: ResponseInit): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:27:78 - error TS2304: Cannot find name 'Response'.
27 <T = JSONValue>(object: T, status?: StatusCode, headers?: HeaderRecord): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:28:39 - error TS2304: Cannot find name 'ResponseInit'.
28 <T = JSONValue>(object: T, init?: ResponseInit): Response;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:28:54 - error TS2304: Cannot find name 'Response'.
28 <T = JSONValue>(object: T, init?: ResponseInit): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:32:61 - error TS2304: Cannot find name 'ResponseInit'.
32 <T>(object: T extends JSONValue ? T : JSONValue, init?: ResponseInit): TypedResponse<T extends JSONValue ? (JSONValue extends T ? never : T) : never>;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:35:66 - error TS2304: Cannot find name 'Response'.
35 (html: string, status?: StatusCode, headers?: HeaderRecord): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:36:27 - error TS2304: Cannot find name 'ResponseInit'.
36 (html: string, init?: ResponseInit): Response;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:36:42 - error TS2304: Cannot find name 'Response'.
36 (html: string, init?: ResponseInit): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:41:20 - error TS2304: Cannot find name 'FetchEvent'.
41 executionCtx?: FetchEvent | ExecutionContext | undefined;
~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:63:22 - error TS2304: Cannot find name 'Request'.
63 constructor(req: Request, options?: ContextOptions<E>);
~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:65:18 - error TS2304: Cannot find name 'FetchEvent'.
65 get event(): FetchEvent;
~~~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:67:16 - error TS2304: Cannot find name 'Response'.
67 get res(): Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:68:19 - error TS2304: Cannot find name 'Response'.
68 set res(_res: Response | undefined);
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:82:58 - error TS2304: Cannot find name 'Response'.
82 redirect: (location: string, status?: StatusCode) => Response;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:84:21 - error TS2304: Cannot find name 'Response'.
84 notFound: () => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/context.d.ts:84:40 - error TS2304: Cannot find name 'Response'.
84 notFound: () => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:48:26 - error TS2304: Cannot find name 'FetchEvent'.
48 handleEvent: (event: FetchEvent) => Response | Promise<Response>;
~~~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:48:41 - error TS2304: Cannot find name 'Response'.
48 handleEvent: (event: FetchEvent) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:48:60 - error TS2304: Cannot find name 'Response'.
48 handleEvent: (event: FetchEvent) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:49:22 - error TS2304: Cannot find name 'Request'.
49 fetch: (request: Request, Env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => Response | Promise<Response>;
~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:49:93 - error TS2304: Cannot find name 'Response'.
49 fetch: (request: Request, Env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:49:112 - error TS2304: Cannot find name 'Response'.
49 fetch: (request: Request, Env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:50:22 - error TS2304: Cannot find name 'Request'.
50 request: (input: Request | string | URL, requestInit?: RequestInit) => Promise<Response>;
~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:50:60 - error TS2304: Cannot find name 'RequestInit'.
50 request: (input: Request | string | URL, requestInit?: RequestInit) => Promise<Response>;
~~~~~~~~~~~
../../node_modules/hono/dist/types/hono.d.ts:50:84 - error TS2304: Cannot find name 'Response'.
50 request: (input: Request | string | URL, requestInit?: RequestInit) => Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:6:10 - error TS2304: Cannot find name 'Request'.
6 raw: Request;
~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:10:26 - error TS2304: Cannot find name 'Request'.
10 constructor(request: Request, path?: string, paramData?: Record<string, string> | undefined);
~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:26:25 - error TS2304: Cannot find name 'FormData'.
26 formData(): Promise<FormData>;
~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:32:20 - error TS2304: Cannot find name 'Headers'.
32 get headers(): Headers;
~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:33:21 - error TS2304: Cannot find name 'RequestRedirect'.
33 get redirect(): RequestRedirect;
~~~~~~~~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:34:17 - error TS2315: Type 'ReadableStream' is not generic.
34 get body(): ReadableStream<Uint8Array> | null;
~~~~~~~~~~~~~~~~~~~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:36:18 - error TS2304: Cannot find name 'RequestCache'.
36 get cache(): RequestCache;
~~~~~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:37:24 - error TS2304: Cannot find name 'RequestCredentials'.
37 get credentials(): RequestCredentials;
~~~~~~~~~~~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:40:17 - error TS2304: Cannot find name 'RequestMode'.
40 get mode(): RequestMode;
~~~~~~~~~~~
../../node_modules/hono/dist/types/request.d.ts:42:26 - error TS2304: Cannot find name 'ReferrerPolicy'.
42 get refererPolicy(): ReferrerPolicy;
~~~~~~~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:17:146 - error TS2304: Cannot find name 'Response'.
17 export declare type Handler<E extends Env = any, P extends string = any, I extends Input = Input, O = {}> = (c: Context<E, P, I>, next: Next) => Response | Promise<Response | TypedResponse<O>> | TypedResponse<O>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:17:165 - error TS2304: Cannot find name 'Response'.
17 export declare type Handler<E extends Env = any, P extends string = any, I extends Input = Input, O = {}> = (c: Context<E, P, I>, next: Next) => Response | Promise<Response | TypedResponse<O>> | TypedResponse<O>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:18:153 - error TS2304: Cannot find name 'Response'.
18 export declare type MiddlewareHandler<E extends Env = any, P extends string = any, I extends Input = {}> = (c: Context<E, P, I>, next: Next) => Promise<Response | void>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:20:79 - error TS2304: Cannot find name 'Response'.
20 export declare type NotFoundHandler<E extends Env = any> = (c: Context<E>) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:20:98 - error TS2304: Cannot find name 'Response'.
20 export declare type NotFoundHandler<E extends Env = any> = (c: Context<E>) => Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:21:88 - error TS2304: Cannot find name 'Response'.
21 export declare type ErrorHandler<E extends Env = any> = (err: Error, c: Context<E>) => Response;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:105:15 - error TS2304: Cannot find name 'Response'.
105 response: Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:105:34 - error TS2304: Cannot find name 'Response'.
105 response: Response | Promise<Response>;
~~~~~~~~
../../node_modules/hono/dist/types/types.d.ts:111:35 - error TS2304: Cannot find name 'File'.
111 form: Record<string, string | File>;
~~~~
../../node_modules/hono/dist/types/utils/body.d.ts:1:56 - error TS2304: Cannot find name 'File'.
1 export declare type BodyData = Record<string, string | File>;
~~~~
../../node_modules/hono/dist/types/utils/body.d.ts:2:38 - error TS2304: Cannot find name 'Request'.
2 export declare function parseBody(r: Request | Response): Promise<BodyData>;
~~~~~~~
../../node_modules/hono/dist/types/utils/body.d.ts:2:48 - error TS2304: Cannot find name 'Response'.
2 export declare function parseBody(r: Request | Response): Promise<BodyData>;
~~~~~~~~
Found 60 errors in 10 files.
Errors Files
1 src/router/dashboard.ts:46
1 src/router/language.ts:1
1 src/types.ts:1
2 ../../node_modules/@trpc/server/dist/adapters/fetch/fetchRequestHandler.d.ts:4
3 ../../node_modules/@trpc/server/dist/adapters/fetch/types.d.ts:4
21 ../../node_modules/hono/dist/types/context.d.ts:17
9 ../../node_modules/hono/dist/types/hono.d.ts:48
10 ../../node_modules/hono/dist/types/request.d.ts:6
9 ../../node_modules/hono/dist/types/types.d.ts:17
3 ../../node_modules/hono/dist/types/utils/body.d.ts:1
Argument of type 'ObjectSchema<{ username: StringSchema<string>; password: StringSchema<string>; }, { username: string; password: string; }>' is not assignable to parameter of type 'BaseSchema<any, any>'.
The types returned by '_parse(...)' are incompatible between these types.
Type '_ParseResult<{ username: string; password: string; }>' is not assignable to type '_ParseResult<any>'.
Type '{ output?: undefined; issues: Issues; }' is not assignable to type '_ParseResult<any>'.ts(2345)
const loginSchema = object({
username: string("Username must be a string", [
minLength(1, "Username is too short"),
]),
password: string("Password must be a string", [
minLength(1, "Password is too short"),
]),
});
app.post("/login", vValidator("json", loginSchema), async (c) => {
const { username, password } = c.req.valid("json");
const [user] = await db
.select()
.from(users)
.where(eq(users.username, username));
if (!user) return c.text("No user", 400);
return c.json(user);
});```
I'm trying to port some code that currently uses Toucan directly to use @hono/sentry
const sentry = new Toucan({
dsn: env.SENTRY_DSN,
request: c.req.raw,
transportOptions: {
headers: {
"X-Client-ID": env.SENTRY_CLIENT_ID,
"X-Client-Secret": env.SENTRY_CLIENT_SECRET,
},
},
environment: env.ENV,
});
But I don't have a way to reference env vars in the @hono/sentry
constructor as it stands, I had to do this:
app.use("*", (c, next) =>
sentry(
{
transportOptions: {
headers: {
"X-Client-ID": c.env.SENTRY_CLIENT_ID,
"X-Client-Secret": c.env.SENTRY_CLIENT_SECRET,
},
},
environment: c.env.ENV,
},
)(c, next),
);
Which works, but isn't great. Also transportOptions
wasn't one of the headers in the types, but thankfully they still got passed through: https://github.com/honojs/middleware/blob/main/packages/sentry/src/index.ts#L49
Thoughts?
When using the new Hono
context generic, and the zValidator
the types dont overlap, the validator seems to lack the context type or able to somehow use it as a generic.
export const archiveRouter = new Hono<{ Bindings: Env }>();
const paramValidator = zValidator('param', z.object({ archiveID: z.string() }));
archiveRouter.put('/:archiveID', paramValidator, teamQueryValidator, async (c) => { ... }
The TS Error:
No overload matches this call.
The last overload gave the following error.
Argument of type 'MiddlewareHandler<Env, string, { in: { param: { archiveID: string; }; }; out: { param: { archiveID: string; }; }; }>'
is not assignable to parameter of type 'H<{ Bindings: Env; }, string, { in: { param: { archiveID: string; }; }; out: { param: { archiveID: string; }; }; }, {}>'.ts(2769)
Temporary solution:
//@ ts-ignore - The Validator middleware internal types clash with the Router types MiddlewareHandler -> { Bindings: Env }
Following the examples linked: here
I get the following error:
Argument of type '(c: Context<Env, "/:id", { in: { param: { id: string; }; }; out: { param: { id: string; }; }; }>) => TypedResponse<{ id: string; age: number; name: string; }>' is not assignable to parameter of type 'Handler<Env, "/:id", { in: { param: { id: string; }; }; out: { param: { id: string; }; }; }, { id: string; name: string; age: number; }>'. Type 'TypedResponse<{ id: string; age: number; name: string; }>' is missing the following properties from type '{ id: string; name: string; age: number; }': id, name, age ts(2345)
It appears the TypedResponse
helper type is causing this error
(I am quite new to OpenAPI, so this is all a bit arcane for me)
When following the example provided by @hono/zod-openapi and using the path parameter syntax '{id}' in Swagger to define an endpoint path like '/users/{id}', an issue arises where the defined endpoint cannot be found. This results in a 404 error when testing the endpoint with Postman or any. However, when the path is manually changed to '/users/:id', the endpoint functions correctly.
Utilize the example provided by @hono/zod-openapi for defining an endpoint path with a path parameter using '{id}' syntax.
Try to access the defined endpoint using Postman or any other API testing tool.
The endpoint defined with the '{id}' syntax should be successfully recognized and accessible both in Swagger documentation and when tested.
When using the defined '{id}' path parameter syntax in Swagger, the endpoint cannot be found, and a 404 error is returned when testing the endpoint api call. However, changing the path parameter syntax to '/users/:id' makes the endpoint work as expected.
This issue seems to be specific to the use of the '{id}' path parameter syntax in Swagger, as manually changing it to '/users/:id' in both the Swagger documentation and the testing tool resolves the issue.
When modifying the OpenAPI documentation directly to use '/users/{id}' and accessing the endpoint, it functions correctly.
It appears that the inconsistency lies in how the '{id}' syntax is interpreted when used within the Swagger documentation.
Library: @hono/zod-openapi
OpenAPI Definition: (Provide the relevant OpenAPI definition excerpt where the endpoint is defined)
Testing Tool: Postman (or specify the testing tool used)
API Endpoint Path: '/users/{id}'
HTTP Method: GET (or specify the method)
Bug
Swagger
OpenAPI
Path Parameter
Your prompt attention to this issue would be greatly appreciated. If additional information is required, please let me know. Thank you for your assistance in resolving this matter.
Hi, I'm having a problem making sure the return type of a handler to be as strict as the defined zod schema in createRoute
. Consider the following code:
const app = new OpenAPIHono().openapi(
createRoute({
summary: 'Test route',
method: 'post',
path: '/test',
responses: {
200: {
description: 'test',
content: {
'application/json': {
schema: z
.object({
name: z.string(),
})
.strict(), // <= a supposedly strict schema for { name: string; }
},
},
},
},
}),
(c) => {
return c.jsonT({
name: 'John Doe',
});
}
);
However, it seems that zod-openapi
doesn't do a type parse/validation on return type. The following handler which returns an extra property/key also works, and doesn't throw an error:
(c) => {
return c.jsonT({
name: 'John Doe',
+ age: 12,
});
}
This could make the API less safe/secure. For example after fetching user data from a DB table, the developer could accidentally return the user data without omitting the password fields first, because TypeScript doesn't complain about the extra "password" field.
I think this is related to the usage of z.infer
in the OutputType<R>
type, which I guess can be fixed by either:
z.parse()
on the response) (using an extra middleware?)Is there a workaround for making sure the type is exactly the same? Or is there something I'm missing? Anyway thank you for the amazing works!
Hi!
import { Hono } from "hono";
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
const app = new Hono<{ Bindings: { aaa: string } }>().basePath("/api");
const schema = z.object({ userID: z.number().int() });
app.post("/:userID", zValidator("param", schema), async (c) => {
const { userID } = c.req.valid("param");
return c.jsonT({ name: userID });
});
export type AppType = typeof route;
I implemented this code, but an error occurs at the zValidator
part like
Diagnostics:
1. No overload matches this call.
The last overload gave the following error.
Argument of type 'MiddlewareHandler<{ Bindings: { aaa: string; }; }, "/:userID", { in: { param: { userID: number; }; }; out: { param: { userID: number; }; }; }>' is not assignable to parameter of type 'H<{ Bindings: { aaa: string; }; }, "/api/:userID", Input, { name: never; }>'.
Type 'MiddlewareHandler<{ Bindings: { aaa: string; }; }, "/:userID", { in: { param: { userID: number; }; }; out: { param: { userID: number; }; }; }>' is not assignable to type 'MiddlewareHandler<{ Bindings: { aaa: string; }; }, "/api/:userID", Input>'.
Types of parameters 'c' and 'c' are incompatible.
Type 'Context<{ Bindings: { aaa: string; }; }, "/api/:userID", Input>' is not assignable to type 'Context<{ Bindings: { aaa: string; }; }, "/:userID", { in: { param: { userID: number; }; }; out: { param: { userID: number; }; }; }>'.
The types returned by 'req.valid(...)' are incompatible between these types.
Type 'unknown' is not assignable to type '{ userID: number; }'. [2769]
When using explode: true
, you end up getting "Expected array, received string"
if you pass in a single value for the enum.
const anon_object = z.object({
paramA: z.array(z.enum([
"ENUM_VAL1",
"ENUM_VAL2",
"ENUM_VAL3",
"ENUM_VAL4",
"ENUM_VAL5",
"ENUM_VAL6",
"ENUM_VAL7",
"ENUM_VAL8",
"ENUM_VAL9",
"ENUM_VAL10",
])).optional().openapi({
description: "An anonymous set of options.",
default: ["ENUM_VAL4"],
param: {
style: "form",
explode: true
}
})
})
We figured out this really gross workaround but it's not super pretty..
const wat = z.array(z.enum([
"ENUM_VAL1",
"ENUM_VAL2",
"ENUM_VAL3",
"ENUM_VAL4",
"ENUM_VAL5",
"ENUM_VAL6",
"ENUM_VAL7",
"ENUM_VAL8",
"ENUM_VAL9",
"ENUM_VAL10",
]));
const anon_object = z.object({
paramA: z.union([wat, z.array(wat)]).optional().openapi({
description: "An anonymous set of options.",
default: ["ENUM_VAL4"],
param: {
style: "form",
explode: true
}
})
})
Any ideas on how we could fix this properly?
Hey! I followed the docs for the new OpenAPI middleware, but now I have a problem with the dynamic route parameter. With /:id
hono works as expected, but the generated OpenAPI JSON is incorrect, since it expects this format: /{id}
.
If I change the path in createRoute
to /{id}
the JSON is valid but hono won't resolve this route.
Is there any way, how I could solve that?
import { serveStatic } from 'hono/bun';
import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
import { cors } from 'hono/cors';
const port = parseInt(process.env.PORT) || 3000;
const ParamsSchema = z.object({
id: z
.string()
.min(3)
.openapi({
param: {
name: 'id',
in: 'path'
},
example: '1212121'
})
});
const UserSchema = z
.object({
id: z.string().openapi({
example: '123'
}),
name: z.string().openapi({
example: 'John Doe'
}),
age: z.number().openapi({
example: 42
})
})
.openapi('User');
const ErrorSchema = z.object({
code: z.number().openapi({
example: 400
}),
message: z.string().openapi({
example: 'Bad Request'
})
});
const route = createRoute({
method: 'get',
path: '/users/:id',
request: {
params: ParamsSchema
},
responses: {
200: {
content: {
'application/json': {
schema: UserSchema
}
},
description: 'Retrieve the user'
},
400: {
content: {
'application/json': {
schema: ErrorSchema
}
},
description: 'Returns an error'
}
}
});
const app = new OpenAPIHono();
app.use('*', cors());
app.use('/favicon.ico', serveStatic({ path: './public/favicon.ico' }));
app.get('/', (c) => {
return c.json({ message: 'Hello World!' });
});
app.doc('/doc', {
openapi: '3.0.0',
info: {
version: '1.0.0',
title: 'My API'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Local dev'
}
]
});
const routes = app.openapi(route, (c) => {
const { id } = c.req.valid('param');
return c.jsonT({
id,
age: 20,
name: 'Ultra-man'
});
});
console.log(`Running at http://localhost:${port}`);
export default {
port,
fetch: app.fetch
};
{
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "My API"
},
"servers": [
{
"url": "http://localhost:3000",
"description": "Local dev"
}
],
"components": {
"schemas": {
"User": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "123"
},
"name": {
"type": "string",
"example": "John Doe"
},
"age": {
"type": "number",
"example": 42
}
},
"required": [
"id",
"name",
"age"
]
}
},
"parameters": {}
},
"paths": {
"/users/:id": {
"get": {
"parameters": [
{
"schema": {
"type": "string",
"minLength": 3,
"example": "1212121"
},
"required": true,
"name": "id",
"in": "path"
}
],
"responses": {
"200": {
"description": "Retrieve the user",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"400": {
"description": "Returns an error",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "number",
"example": 400
},
"message": {
"type": "string",
"example": "Bad Request"
}
},
"required": [
"code",
"message"
]
}
}
}
}
}
}
}
}
}
Thank you for creating a very usable library.
I am now using @hono/zod-openapi for my project.
It is possible to insert middleware as follows
const app = new OpenAPIHono()
app.use("*", anyMiddleware)
app.openapi(route, c => {
// something
})
Is it possible to change the interface to accept middleware as handler, like Hono.get
or Hono.post
? I'd like to create a PR, but I'm wondering if there are any barriers that have prevented me from doing so in the past?
const app = new OpenAPIHono()
app.use("*", anyMiddleware)
app.openapi(route, anyMiddleware, c => {
// something
})
```
please add a feature graphql caching in workers kv
Hi.
It would be nice to make it easy to handle uploads.
References:
I try to use hono/zod-openapi but got error
error: Could not resolve 'npm:@hono/[email protected]'.
Caused by:
not found
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.