path-to-regexp approach suffers from as route counts grow.
Route patterns
The TrieRouter supports the same expressive pattern syntax you’d expect from an Express-style framework, with one addition: regular expression constraints on named parameters.Static segments always take priority over named parameters, which take
priority over wildcards. This means
/users/me always matches the static
route even when /users/:id is also registered.Sub-routers
app.Router() creates an independent sub-router with its own Layer stack. Mount it with app.use(prefix, router) to scope its routes and middleware under a URL prefix. Sub-routers automatically inherit the parent’s env and ctx — you don’t need to pass them manually.
1
Create a sub-router
Call
app.Router() to instantiate the sub-router, then register routes on it just as you would on app:routes/users.ts
2
Add scoped middleware
Middleware registered on the sub-router only runs for requests matched by that router. This keeps auth, logging, and validation logic close to the routes they protect:
routes/api.ts
3
Mount the sub-router
Pass the sub-router to
app.use() with a path prefix. Blaze strips the prefix from req.path before dispatching into the sub-router, so routes registered as / on the sub-router respond to the mount prefix on the parent:src/index.ts
Route chaining
router.route(path) (or app.route(path)) returns a chainable Route object. Register GET, POST, PUT, PATCH, and DELETE handlers on the same path without repeating it:
Middleware scope
Blaze gives you three levels of middleware granularity. Use whichever scope is most appropriate for each concern:- Global
- Path-scoped
- Route-level
Registered with
app.use() and no path — runs on every request before any route handler:Middleware layers run in registration order. Global middleware registered
before
app.use('/prefix', router) runs before the sub-router’s own
middleware. Register error handlers last so they can catch errors from all
earlier layers.