Hono Mount a Kizuna API on a Hono application.
@kizunajs/hono serves a Kizuna API from a Hono application. Hono runs on Cloudflare Workers, Deno, Bun, Node.js, and other runtimes.
Requires Hono >= 4.
pnpm add @kizunajs/hono@beta hono
honoAdapter() is what you name in kizuna.config.ts. Naming it is what gives every handler on this API Hono's own Context, typed.
import { k } from '../k' ;
export const users = k.routes. users ({
listUsers: k
. route ({
method: 'GET' ,
path: '/users' ,
query: ListUsersQuerySchema,
responses: {
200 : UserListSchema,
},
})
. handler ( async ({ query }) => ({
status: 200 ,
body: {
users: await db.users. findMany ({
skip: (query.page - 1 ) * query.limit,
take: query.limit,
}),
total: await db.users. count (),
},
})),
getUser: k
. route ({
method: 'GET' ,
path: '/users/:id' ,
responses: {
200 : UserSchema,
404 : ProblemDetailsSchema,
},
})
. handler ( async ({ params }) => {
const user = await db.users. findById (params.id);
if ( ! user) {
return {
status: 404 ,
body: {
detail: 'Not found' ,
},
};
}
return {
status: 200 ,
body: user,
};
}),
}); import { defineConfig } from 'kizunajs' ;
import { honoAdapter } from '@kizunajs/hono' ;
import { users } from './src/routes/users' ;
export default defineConfig ({
adapter: honoAdapter (),
routes: [users],
}); import { Hono } from 'hono' ;
import kizuna from '../kizuna.config' ;
const app = new Hono ();
kizuna.api. mount (app);
export default app;
Each handler receives { params, query, body, headers, c }. The c is Hono's Context, useful when you need to read cookies, reach environment bindings, or use anything else Hono exposes.
. handler ( async ({ params , c }) => {
const locale = c.req. header ( 'accept-language' );
return {
status: 200 ,
body: await db.users. findById (params.id),
};
});
On runtimes like Cloudflare Workers, bindings live on the Hono context as c.env.
getUser : k
. route ({
method: 'GET' ,
path: '/users/:id' ,
responses: {
200 : UserSchema,
},
})
. handler ( async ({ params , c }) => ({
status: 200 ,
body: await c.env. DB . prepare ( 'SELECT * FROM users WHERE id = ?' ). bind (params.id). first (),
})),
A guard sits on the identity it authenticates, and receives the credential extracted and typed, alongside the native c. See the Authentication guide .
import { k } from './k' ;
export const user = k.identity
. bearer ({
context: z. object ({
userId: z. string (),
}),
})
. guard ( async ({ bearer , deny , c }) => {
const session = bearer ? await verifySession (bearer.token, c.env) : undefined ;
if ( ! session) {
return deny ({
status: 401 ,
body: {
detail: 'Unauthorized' ,
},
});
}
return {
userId: session.userId,
};
});
For request-scoped values handlers need, use request context . For other middleware, such as logging, rate limiting, and caching headers, use Hono's own app.use. Authentication belongs in guards .
kizuna.api. mount (app, {
responseValidation: false ,
});
Option Default Description responseValidationfalseValidate handler return values against response schemas. Enable in development. formatErrornone Reshape error (>= 400) response bytes for migrating clients. Most don't need it (use Problem Details extension members). See Migrating an existing API .
A stream response is returned as a ReadableStream body. The generator's signal is c.req.raw.signal, which the Node server aborts when the client goes away.