How to Add Routes
HTTP routes live in src/routes/<domain>/index.ts. Each file exports a Hono route chain that gets mounted in
src/routes/index.ts.
Anatomy of a route file
import { createProjectSchema } from "@backend/features/projects/db/mutations/create-project.mutation";
import { createProject } from "@backend/features/projects/modules/create-project";
import { ApplicationError, ApplicationErrorCode } from "@backend/middleware/error";
import { hasAccess } from "@backend/middleware/permissions/has-access";
import { validate } from "@backend/middleware/validation/validate";
import { getAuthenticatedApp } from "@backend/utils/auth/auth-hono";
type ProjectMutationResponse = { projectId: string };
export const projectRoutes = getAuthenticatedApp()
.get("/", hasAccess({ action: "read", subject: "project" }), async (ctx) => {
const { organizationId } = ctx.var;
const response = await getProjectsList({ organizationId });
return ctx.json(response);
})
.post("/", hasAccess({ action: "write", subject: "project" }), validate("json", createProjectSchema), async (ctx) => {
const body = ctx.req.valid("json");
const { projectId } = await createProject({ data: body });
return ctx.json<ProjectMutationResponse>({ projectId });
});Registering routes
Mount routes in src/routes/index.ts:
import { projectRoutes } from "./projects";
export const registerRoutes = (app: Hono) => {
return app.route("/projects", projectRoutes);
// ... other routes
};The return type of registerRoutes is what generates the ApiRoutesSdk type for the frontend. Adding a route here
automatically exposes it on the frontend SDK.
Validation is mandatory
Every route that accepts input must use the validate() middleware:
// Body validation
validate("json", createProjectSchema);
// Query param validation
validate("query", filtersSchema);Read validated data with ctx.req.valid("json") or ctx.req.valid("query"). Never use ctx.req.json() directly, since
it bypasses validation and breaks the type bridge.
Authentication and permissions
getAuthenticatedApp()creates a route group that requires authenticationhasAccess({ action, subject })checks user permissions before the handler runs- For public routes, use a plain
new Hono()instead
Adding a new route
- Create
src/routes/<domain>/index.ts - Define routes using
getAuthenticatedApp()withvalidate()middleware - Import and register in
src/routes/index.tswithapp.route("/prefix", routes) - The frontend SDK picks up the new endpoint automatically
Every purchase and sponsorship funds my 8+ years of work on open source given freely to the community. It keeps the lights on, funds new packs, and keeps the ecosystem alive. Even a small tier means a lot. Thank you!
