madooei avatar

create-routes

Create route definitions for HTTP endpoints. Use when wiring controllers and middleware to HTTP rout

提供方 madooei|开源

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:

  1. Controller created (src/controllers/{entity-name}.controller.ts)
  2. Schemas for validation (src/schemas/{entity-name}.schema.ts)
  3. 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 use new 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 handlers
  • create-middleware - Create validation and auth middleware
  • test-routes - Test route configurations
create-routes - 适用于 Claude Code 与 Cursor 的 AI 智能体 Skill | Agent Skills