| name | create-routes |
| description | Create route definitions for HTTP endpoints. Use when wiring controllers and middleware to HTTP routes. Triggers on "create routes", "router for", "route definitions". |
Create Routes
Creates Hono route definitions that wire controllers and middleware to HTTP endpoints. Routes use a factory function pattern for dependency injection.
Quick Reference
Location: src/routes/{entity-name}.router.ts
Naming: Singular for entities (note.router.ts), plural for cross-cutting (events.router.ts)
Prerequisites
Before creating routes, ensure you have:
- Controller created (
src/controllers/{entity-name}.controller.ts) - Schemas for validation (
src/schemas/{entity-name}.schema.ts) - Middleware (auth, validation) available
Instructions
Step 1: Create the Router File
Create src/routes/{entity-name}.router.ts
Step 2: Import Dependencies
import { Hono } from "hono";
import type { {Entity}Controller } from "@/controllers/{entity-name}.controller";
import type { AppEnv } from "@/schemas/app-env.schema";
import { validate as defaultValidate } from "@/middlewares/validation.middleware";
import { entityIdParamSchema } from "@/schemas/shared.schema";
import {
create{Entity}Schema,
{entity}QueryParamsSchema,
} from "@/schemas/{entity-name}.schema";
import { authMiddleware as defaultAuthMiddleware } from "@/middlewares/auth.middleware";
Step 3: Define Dependencies Interface
export interface Create{Entity}RoutesDeps {
{entity}Controller: {Entity}Controller;
validate?: typeof defaultValidate;
authMiddleware?: typeof defaultAuthMiddleware;
}
Step 4: Create Factory Function
export const create{Entity}Routes = (dependencies: Create{Entity}RoutesDeps) => {
const {
{entity}Controller,
validate = defaultValidate,
authMiddleware = defaultAuthMiddleware,
} = dependencies;
const {entity}Routes = new Hono<AppEnv>();
// Apply auth middleware to all routes
{entity}Routes.use("*", authMiddleware);
// Define routes...
return {entity}Routes;
};
Step 5: Define CRUD Routes
// GET / - List all
{entity}Routes.get(
"/",
validate({
schema: {entity}QueryParamsSchema,
source: "query",
varKey: "validatedQuery",
}),
{entity}Controller.getAll,
);
// GET /:id - Get by ID
{entity}Routes.get(
"/:id",
validate({
schema: entityIdParamSchema("id"),
source: "params",
varKey: "validatedParams",
}),
{entity}Controller.getById,
);
// POST / - Create
{entity}Routes.post(
"/",
validate({
schema: create{Entity}Schema,
source: "body",
varKey: "validatedBody",
}),
{entity}Controller.create,
);
// PUT /:id - Update (full replace)
{entity}Routes.put(
"/:id",
validate({
schema: entityIdParamSchema("id"),
source: "params",
varKey: "validatedParams",
}),
validate({
schema: create{Entity}Schema,
source: "body",
varKey: "validatedBody",
}),
{entity}Controller.update,
);
// DELETE /:id - Delete
{entity}Routes.delete(
"/:id",
validate({
schema: entityIdParamSchema("id"),
source: "params",
varKey: "validatedParams",
}),
{entity}Controller.delete,
);
Patterns & Rules
Factory Function Pattern
Always use a factory function with dependency injection:
export const create{Entity}Routes = (dependencies: Create{Entity}RoutesDeps) => {
const { {entity}Controller, validate = defaultValidate } = dependencies;
const router = new Hono<AppEnv>();
// ...
return router;
};
This enables:
- Testing with mock dependencies
- Flexible middleware injection
- Consistent instantiation pattern
Type Controllers in Interface
Use type import for controllers to avoid circular dependencies:
import type { {Entity}Controller } from "@/controllers/{entity-name}.controller";
export interface Create{Entity}RoutesDeps {
{entity}Controller: {Entity}Controller; // Not a class, just the type
}
Default Middleware Imports
Import middleware with aliases and provide as defaults:
import { validate as defaultValidate } from "@/middlewares/validation.middleware";
import { authMiddleware as defaultAuthMiddleware } from "@/middlewares/auth.middleware";
// In factory function
const { validate = defaultValidate, authMiddleware = defaultAuthMiddleware } =
dependencies;
Global vs Per-Route Middleware
Apply shared middleware globally, specific middleware per-route:
// Global auth for all routes
{
entity;
}
Routes.use("*", authMiddleware);
// Per-route validation
{
entity;
}
Routes.get(
"/",
validate({ schema: querySchema, source: "query", varKey: "validatedQuery" }),
controller.getAll,
);
Validation Middleware Chaining
For routes needing multiple validations, chain middleware:
{entity}Routes.put(
"/:id",
validate({ schema: entityIdParamSchema("id"), source: "params", varKey: "validatedParams" }),
validate({ schema: updateSchema, source: "body", varKey: "validatedBody" }),
{entity}Controller.update,
);
Route Typing
Always type routers with AppEnv:
const {entity}Routes = new Hono<AppEnv>();
Mounting Routes in App
Routes are mounted in src/app.ts:
import { createNoteRoutes } from "@/routes/note.router";
import { NoteController } from "@/controllers/note.controller";
import { NoteService } from "@/services/note.service";
// Create dependency chain
const noteService = new NoteService();
const noteController = new NoteController(noteService);
// Mount routes
app.route("/notes", createNoteRoutes({ noteController }));
Complete Example
See REFERENCE.md for a complete note.router.ts implementation.
What NOT to Do
- Do NOT instantiate controllers inside route files (inject them)
- Do NOT use untyped
new Hono()(always usenew Hono<AppEnv>()) - Do NOT hardcode middleware (allow injection for testing)
- Do NOT forget to return the router from the factory function
- Do NOT put business logic in routes (that's for controllers/services)
See Also
create-controller- Create controllers for route handlerscreate-middleware- Create validation and auth middlewaretest-routes- Test route configurations