Hono
Use rest-rpc with Hono
Install
npm install @rest-rpc/honopnpm add @rest-rpc/honoyarn add @rest-rpc/honobun add @rest-rpc/hononub add @rest-rpc/honoaube add @rest-rpc/honoUsage
import { Hono } from "hono";
import { route, registerRoutes } from "@rest-rpc/hono";
import { z } from "zod";
const app = new Hono();
const api = new Hono();
const todos = new Map([["todo_1", { id: "todo_1", title: "Write docs" }]]);
const getTodo = route
.get("/todos/:id")
.params(z.object({ id: z.string() }))
.handler(({ params }) => {
const todo = todos.get(params.id);
return todo
? { status: 200, body: todo }
: { status: 404, body: { code: "TODO_NOT_FOUND" as const } };
});
export const routes = { todos: { get: getTodo } };
registerRoutes(api, routes);
app.route("/api", api);
export default app;
registerRoutes() accepts a Hono app. Mount it with app.route() to serve
routes under a prefix.
Framework Context
Handlers and middleware receive the Hono context c in addition to the
common handler arguments:
const getTodo = route
.get("/todos/:id")
.params(z.object({ id: z.string() }))
.handler(({ params, c }) => {
console.log(c.req.method, c.req.header("x-request-id"));
return todos.get(params.id);
});
To share an application context type across routes, augment
DefaultContext in @rest-rpc/hono.
Options
type RegisterRoutesOptions<TEnv extends Env = Env> = {
bodyCodecs?: readonly BodyCodec<HonoRequest>[];
disableRequestValidation?: boolean;
disableResponseValidation?: boolean;
requestBodyLimit?: number;
requestValidationErrorHandler?: RequestValidationErrorHandler<TEnv>;
responseValidationErrorHandler?: ResponseValidationErrorHandler<TEnv>;
middleware?: ExtendedHonoMiddleware<TEnv>[];
};
bodyCodecs,requestBodyLimit: see Request and Response Bodies.disableRequestValidation,disableResponseValidation: see Disabling Server Validation.requestValidationErrorHandler,responseValidationErrorHandler: see Error Handling.middleware: see Middleware.
Middleware
Use the middleware option to run Hono middleware only for rest-rpc routes.
Each function has the Hono middleware signature plus a route argument that
contains the matched route declaration, including its metadata.
The middleware is registered on each rest-rpc route, so it does not run for other routes on the same app. It runs before rest-rpc decodes and validates the request, and before route middleware.
registerRoutes(app, routes, {
middleware: [
async (c, next, route) => {
if (
route.metadata?.auth === "required" &&
!c.req.header("authorization")
) {
return c.json({ code: "UNAUTHORIZED" }, 401);
}
await next();
},
],
});
Request and Response Bodies
The adapter decodes request bodies with the
default codecs, so no
body-parsing middleware is needed. Built-in decoding reads at most
requestBodyLimit bytes, 1 MiB by default. A custom codec’s deserialize
receives the HonoRequest (c.req) and is not subject to requestBodyLimit.
Handlers can return any value supported by the default codecs. A custom
serializer’s body must be a Fetch Response body, such as a string, Blob,
FormData, byte array, or ReadableStream.
Error Handling
Use requestValidationErrorHandler and responseValidationErrorHandler to
replace the default validation error responses.
Other errors use Hono’s error handler.
registerRoutes(app, routes, {
requestValidationErrorHandler: (error, c) => {
return c.json({ code: "VALIDATION_ERROR", issues: error.issues }, 422);
},
responseValidationErrorHandler: (_error, c) => {
return c.json({ code: "INVALID_RESPONSE" }, 500);
},
});
app.onError((error, c) => {
return c.json({ code: "INTERNAL_SERVER_ERROR" }, 500);
});