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

Dynamic routes

URL parameters, splat segments, and how to define them in Nooh's filesystem routing.

When a route needs to capture variable parts of the URL, you use special bracket or dollar syntax in the filename. Nooh converts these to Hono path parameters at compile time.

Named parameters

Wrap a segment name in square brackets to create a named URL parameter:

File Route Parameter
routes/users/[id].get.ts GET /users/:id id
routes/posts/[slug].get.ts GET /posts/:slug slug
routes/orgs/[org]/repos/[repo].get.ts GET /orgs/:org/repos/:repo org, repo

Inside the handler, read them exactly as in any Hono app:

// src/routes/users/[id].get.ts
import { get } from "@router/users/[id]";

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

Hono’s type inference keeps c.req.param("id") typed as string. Requesting a parameter name that does not exist in the file path is a TypeScript error.

Splat segments (wildcards)

A filename of $.get.ts (or any name ending in $) creates a wildcard segment that matches the rest of the path:

File Route Captures
routes/files/$.get.ts GET /files/* Everything after /files/
// src/routes/files/$.get.ts
import { get } from "@router/files/$";

export default get((c) => {
  const filePath = c.req.param("*");
  return c.text(`Serving: ${filePath}`);
});

Combining static and dynamic segments

Static and dynamic segments can be mixed freely:

  • routes/
    • orgs/
      • [org]/
        • repos/
          • [repo]/
            • index.get.ts
            • commits.get.ts

This produces:

  • GET /orgs/:org/repos/:repo
  • GET /orgs/:org/repos/:repo/commits

Both :org and :repo are available via c.req.param() in every handler inside the nested structure.

Route generation for dynamic paths

The @router import path mirrors the filesystem path exactly — brackets and all:

import { get } from "@router/users/[id]";
import { get } from "@router/orgs/[org]/repos/[repo]";

This is how Nooh knows which parameters belong to which route — the import path is the contract, and the compiler enforces it.

Was this page helpful?