Hype StackHypeStack

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

ts
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:

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:

ts
// 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 authentication
  • hasAccess({ action, subject }) checks user permissions before the handler runs
  • For public routes, use a plain new Hono() instead

Adding a new route

  1. Create src/routes/<domain>/index.ts
  2. Define routes using getAuthenticatedApp() with validate() middleware
  3. Import and register in src/routes/index.ts with app.route("/prefix", routes)
  4. The frontend SDK picks up the new endpoint automatically
Sponsor open source

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!

Sponsor on GitHub