DocumentsPersonal
Route Registry
Route Registry
Type
Topic
Status
Published
Created
Jul 9, 2026
Updated
Jul 9, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Route Registry#

The route registry is the mechanism that maps route modules in lib/routes/ into a live Hono application. It has three environment-specific modes, a shared helper layer, and a build-time serialization step.

Core Files#

FileRole
lib/registry.tsEntry point — selects the right loading strategy and mounts all routes onto the Hono app
lib/registry-helpers.tsShared logic: NamespacesType, applyModulesToNamespaces, registerRssRoutes, registerApiRoutes, sortRoutes
lib/registry-dev.tsDev-only lazy loader using a per-top-directory sub-app strategy
scripts/workflow/build-routes.tsBuild-time script that serializes all namespaces to assets/build/routes.js

Environment Modes#

registry.ts branches on NODE_ENV / VERCEL_ENV :

  • production / config.isPackage — eagerly imports the pre-built assets/build/routes.js. Route handlers are stubs; the real handler is loaded lazily on first request via a module() thunk injected at build time .
  • test — imports assets/build/routes.json (JSON, no thunks). Missing handlers are re-imported directly by path at request time .
  • default (dev) — creates a DevRegistry via createDevRegistry() instead of pre-loading all routes .

Dev Registry (registry-dev.ts)#

Dev mode cannot register all routes upfront because Hono freezes its router after the first matched request ("matcher is already built") . The solution:

  • On startup, readdirSync the lib/routes/ directory once to enumerate top-level directories .
  • Each top-level directory gets its own isolated Hono sub-app, built on first request for that namespace via loadTopDirectory() . Sub-apps are memoized in a Map<string, Promise<Hono>> .
  • loadTopDirectory calls directoryImport to bulk-import all .ts/.tsx files in that directory, then calls applyModulesToNamespaces + registerRssRoutes + registerApiRoutes .
  • The outer middleware inspects the first path segment to pick the right sub-app and bridges context variables between the outer and inner Hono instances .
  • ensureAllLoaded() is exported so the build script can force-load every directory before serialization .

Namespace and Route Resolution (registry-helpers.ts)#

Route modules are organized into namespaces. A directory under lib/routes/ is a namespace root when it contains a namespace.ts file . Each module is assigned to its longest matching namespace root; modules with no matching root fall back to their first path segment .

applyModulesToNamespaces merges imported modules into the NamespacesType record. A module may export { route }, { apiRoute }, or { namespace } .

Route registration order matters. sortRoutes prioritizes literal path segments over parameter segments (:param), and regex-constrained parameters over plain ones . Namespaces are registered deepest-first so parent param-routes cannot shadow nested namespaces .

Build-Time Serialization (scripts/workflow/build-routes.ts)#

Run via pnpm build:routes (cross-env NODE_ENV=dev tsx scripts/workflow/build-routes.ts) .

The script :

  1. Calls ensureAllLoaded() to fully populate namespaces via the dev registry.
  2. Injects a module thunk string (() => import('@/routes/<ns>/<location>')) onto each route/apiRoute . These string literals are embedded verbatim in the output JS using a regex replacement, so they become real dynamic import() calls in production.
  3. Writes output to assets/build/:
    • routes.js — default export with module thunks (production)
    • routes.json — plain JSON without thunks (test/tooling)
    • radar-rules.json / radar-rules.js — browser-extension radar rules
    • maintainers.json — maintainer index
    • route-paths.ts — auto-generated TypeScript union type RoutePath

A Worker-specific variant (WORKER_BUILD=true) writes routes-worker.js instead .

CI: Build Assets Workflow#

The build-assets.yml workflow triggers on every push to master that touches lib/**/*.ts(x). It runs pnpm build (which runs build:routes then tsdown), commits the assets/ output to the gh-pages branch, and then syncs docs artifacts to the RSSNext/rsshub-docs repository.

NSFW Filtering#

After namespaces are loaded (in non-dev modes), registry.ts applies safeNamespaces() if config.feature.disable_nsfw is set, stripping any namespace where every route carries features.nsfw .

Route Registry | Dosu