Skip to content
nooh
Esc
↑↓navigate↵open⌘Jpreview
On this page

Route Generation

How Nooh compiles file-based routes into typed Hono modules and native route handlers.

When you run nooh build, nooh dev, or use the Nooh bundler plugin, the Nooh compiler inspects your routes directory and generates fully typed route modules inside .nooh/.

The .nooh Directory

The compiler produces a mirror of your routes structure along with helper artifacts:

.nooh
app.ts
router
index.ts
middleware.ts
users
index.ts
[id].ts

What is generated?

  1. app.ts: The main entry point creating the root Hono instance, registering all global middleware, mounting sub-routers, and exporting the final application.
  2. router/middleware.ts: Helper wrappers for defining typed middlewares matching your global environment.
  3. router/**/*.ts: Per-route helper modules exporting method functions (such as get, post, put, del, patch, options, head, all).

The @router/* Alias

Nooh uses a path alias configured in your tsconfig.json so you can cleanly import route helpers from anywhere without relative clutter:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@router/*": ["./.nooh/router/*"]
    }
  }
}

When authoring src/routes/users/[id].get.ts, you import from @router/users/[id]:

import { get } from "@router/users/[id]";

export default get((c) => {
  const { id } = c.req.param();
  return c.json({ id });
});

Anatomy of Generated Route Modules

Each generated file in .nooh/router/ provides method builder functions tailored specifically for that route.

Type Signature & Overloads

Generated method functions support multiple call styles:

// 1. Plain handler
export default get((c) => c.text("Hello"));

// 2. Options object with validation, dependencies, or route-level middleware
export default get({
  schema: {
    param: userParamSchema,
  },
  deps: [userService],
  handler: (c) => {
    const { id } = c.req.valid("param");
    const user = c.deps.userService.findById(id);
    return c.json(user);
  },
});

Route Metadata

The generated route handler embeds internal metadata (Symbol.for("nooh.route")) used by the Nooh compiler during the introspection and diffing phases:

const NOOH_ROUTE_METADATA = Symbol.for("nooh.route");

This allows the compiler to inspect declared dependencies, validation schemas, and error boundaries during compilation without having to execute runtime code.

Incremental Compilation & Caching

The compiler performs dependency graph analysis and diffing to keep your development workflow instant:

  • Only changed routes recompile: If you edit a single file in routes/, only that route’s helper module is regenerated.
  • Strict type safety: If you change a route parameter from [id] to [slug], TypeScript immediately reports errors where c.req.param("id") is accessed.
  • Zero runtime overhead: The generated code compiles down to vanilla Hono handlers. No reflection, runtime filesystem scanning, or regex parsing takes place at request time.

Was this page helpful?