Routing and Routers
By Syncfusion®, Inc. · Last updated
Route methods, parameters, query strings, the Express 5 path syntax, route order, handler chains, app.route, router files, router middleware, nested routers, mergeParams and router.param.
What you will learn
- Match methods and paths
- Read route params and query strings
- Use the Express 5 path syntax
- Order routes correctly
- Split routes into Router files and nest them
Routing Introduction
Routing and Routers = deciding which code runs for each request.
- Route = a method + a path + a handler.
app.get("/menu", handler)is one route.- Express checks routes from top to bottom.
- The first route that matches runs.
- No match means a 404 answer.
Simple lesson: A route is a rule: "for this method and path, run this function".
A Real-World Example
A school building has many rooms.
- A sign at the gate says: Library, Room 1. Lab, Room 2.
- A visitor reads the signs from top to bottom.
- They walk to the first room that matches.
- If no sign matches, they go back: "not found".
Routes are the signs. Express is the visitor reading them.
In this lesson, you will learn:
- Route methods, paths and parameters.
- The Express 5 path syntax: named wildcards and optional parts.
- Query strings and safe value conversion.
- Route order, handler chains,
next("route")andapp.route(). - How to move routes into files with
express.Router(). - Router middleware, nested routers,
mergeParamsandrouter.param.
Route Methods
- app.get / post / put / patch / delete = one route per HTTP method.
- app.options / app.head = rarely needed. Express handles both for you.
- app.all(path, handler) = matches every method on that path.
- Use
app.allfor things like "this path is closed for everyone".
Simple lesson: The method name you call is the HTTP method you answer.
Route Paths
- Static path = fixed text, such as
/menu. - Dynamic path = has a route parameter, such as
/menu/:id. - Route parameter = a named part of the path. Its value lands in
req.params. - Multiple parameters:
/menu/:category/:idgives two values. - Regular expression route:
app.get(/^\/item-\d+$/, ...)matches by pattern.
/menu/3setsreq.params.idto"3"./menu/snacks/12sets bothcategoryandid.- Regular expression groups land in
req.params[0],req.params[1]and so on.
Simple lesson: :name in a path catches that part of the URL into req.params.name.
Express 5 Path Syntax
Express 5 changed how special paths are written.
- Named wildcard:
/*splatmatches any path.req.params.splatis an array of parts. - Optional part:
{/:id}means "this part may be missing". - Optional wildcard:
/{*splat}also matches the root/. - Reserved characters:
( ) [ ] ? + !are not allowed in paths. Express throws an error. - Old Express 4 code:
"*"becomes"/*splat", and"/:id?"becomes"{/:id}".
/files/notes/term1.pdfgives the parts["notes", "term1.pdf"]./ordersand/orders/9use the same route.- The last route catches everything else and sends a JSON 404.
A catch-all route must be the last route. Express reads top to bottom, so a catch-all at the top would answer every request.
Simple lesson: Express 5 wildcards need a name, and optional parts use braces.
Query Strings
Query string = the ?name=value&name=value part of a URL.
- Read it from
req.query. - Use it for options: search, filter, sort, page and limit.
- Default value:
req.query.sort || "name". - Repeated names:
?tag=veg&tag=spicygives an array["veg", "spicy"]. - Missing name:
req.query.pageisundefined. - Empty value:
?page=gives the empty string"".
Converting Query Values
Every query value is a string, or an array of strings.
- Numbers:
Number(req.query.page). Check withNumber.isInteger. - Booleans:
req.query.veg === "true". The string"false"is truthy, so do not use it directly. - Arrays:
[].concat(req.query.tag ?? [])always gives an array. - Bad values: fall back to a safe default, or answer 400.
- Try each quick request and compare the results.
page=abcgives a clear 400, not a strange empty list.
Easy way to remember: everything in req.params and req.query is text until you convert it.
Route Parameters or Query?
- Route parameter: names one thing.
/menu/7is item 7. - Query: shapes a list.
/menu?veg=true&sort=price. - If the URL makes no sense without the value, use a route parameter.
- If the value is optional, use the query.
Simple lesson: Convert query strings carefully, and always have a safe default.
Route Order
- Express tries routes in the order you wrote them.
- Put specific routes before general ones.
/menu/todaymust come before/menu/:id.- Otherwise
:idcatches the word"today".
/menu/todayanswers "Item today". That is the bug.- Move the
/menu/todayroute above/menu/:idand run again.
The same rule applies to middleware and routers. Express always works through them in the order your code adds them. See Middleware Fundamentals.
Simple lesson: Specific first, general later, catch-all last.
Multiple Handlers
A route can have more than one handler.
- Handlers run in order, like a short chain.
- Each one calls
next()to pass to the next. - You can pass functions one by one, or in an array, or both.
- next("route") = skip the rest of this route's handlers and try the next route.
- Call
next()only once per handler.
/orders/5runs all three handlers of the first route./orders/0jumps to the second route withnext("route").
Simple lesson: Chain small handlers with next(). Jump to the next route with next("route").
app.route() and Reusable Handlers
app.route(path) = write one path, then chain its methods.
- Fewer repeated path strings, fewer typos.
- Related handlers stay together.
- Put handlers in their own file and import them.
- Named functions make error messages easier to read.
DELETE /ordersgives 404, because no delete handler was chained.- Real projects group routes with a router. See the other part of this lesson.
Simple lesson: app.route keeps one path's methods together.
Express Router
Router = a mini app that holds only routes and middleware.
- Create one with
express.Router(). - Add routes to it, just like
app.getandapp.post. - Mount it with
app.use("/menu", menuRouter). - A router has no settings and cannot
listen. - Big apps use one router per topic, such as menu, orders and students.
Simple lesson: A router is a box of related routes you plug into the app.
A Second Example
A big school has one main office and many department desks.
- The gate guard sends science questions to the science desk.
- Sports questions go to the sports desk.
- Each desk handles its own questions in its own way.
- The guard only needs to know which desk is where.
app is the gate guard. Each router is a desk.
Creating and Mounting a Router
- express.Router() = creates a router.
- router.get(path, handler) = adds a route to it.
- export default router = lets other files use it.
- app.use("/menu", router) = mounts it at
/menu. - Mount path = the path in front of every route in the router.
- Inside the router, paths do not repeat
/menu. - Change the mount to
app.use("/food", menuRouter). Now the routes move to/food. - The router file never changed. That is the point.
Simple lesson: Write routes once in a router, and choose where they live with app.use.
Router Methods and Options
- router.get / post / put / patch / delete / all = the same as on
app. - router.use(fn) = middleware for this router only.
- router.route(path) = chain methods on one path.
- caseSensitive: true =
/Menuand/menuare different. - strict: true =
/menuand/menu/are different. - mergeParams: true = lets a child router read the parent's params.
- Requests to
/ordersprint a log line. /menuprints nothing, because it never enters the orders router.
Simple lesson: Router middleware only runs for that router's paths.
Router-Level Middleware
Router-level middleware = middleware added with router.use.
- For the whole router:
router.use(fn). - For one path in it:
router.use("/special", fn). - For one route:
router.get("/", fn, handler). - It runs in the order you add it, before the routes below it.
- Use it for things one area needs, such as a staff-only check.
Middleware gets its own lesson. See Middleware Fundamentals. For now, remember it is a function with (req, res, next) that runs before your routes.
Simple lesson: Put area-specific checks on the router, not on the whole app.
Nested Routers and mergeParams
A router can be mounted inside another router.
- Parent router:
/menu/:id. - Child router:
/reviews, mounted inside the parent. - Full path:
/menu/:id/reviews. - By default, the child cannot see
:id. - Create the child with
express.Router({ mergeParams: true })to fix this.
- With
mergeParams,itemIdis"2"or"3". - Without it,
itemIdbecomes"unknown".
Without mergeParams, the child router does not crash. It just sees undefined, which is easy to miss. Test nested routes with a real id.
Simple lesson: A child router needs mergeParams: true to read its parent's params.
router.param and app.param
router.param(name, callback) = runs a function whenever a route uses :name.
- The callback gets
(req, res, next, value). - Use it to load the thing the id points to.
- Answer 404 there if it does not exist.
- Every route with
:idthen gets a readyreq.item. - It runs once per request, even if many routes match.
- app.param does the same for routes on
app. router.param only for routes on that router.
- Both routes use
req.item, but the lookup code is written once. /menu/99stops in the param callback with a 404.
Simple lesson: Load once with param, then use the result in every route.
Organising Router Files
- One router per resource:
routes/menu.js,routes/orders.js,routes/students.js. - Name files after the resource, in lowercase.
- Keep handlers separate when routers grow:
handlers/menu.js. - Mount all routers in app.js, so one file shows the whole map.
- Prefix API routes:
app.use("/api/menu", menuRouter).
canteen-api/
├── routes/
│ ├── menu.js
│ └── orders.js
├── handlers/
│ └── menu.js
├── app.js
└── server.js
Easy way to remember: routes/ says where, handlers/ says what.
Simple lesson: One file per resource, all mounted in app.js.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
/menu/:id above /menu/today | Express picks the best match | Express picks the first match | today is read as an id |
| Catch-all route at the top | It only catches leftovers | It catches everything below it | Every request gets 404 |
app.get("*", ...) in Express 5 | Old wildcards still work | Use "/*splat" | TypeError: Missing parameter name |
if (req.query.veg) for ?veg=false | "false" is false | Any non-empty string is truthy | Non-veg items are hidden |
req.query.page * limit without checks | Query values are numbers | They are strings, maybe junk | NaN and empty pages |
Calling next() twice | It is harmless | Two handlers run, two responses | ERR_HTTP_HEADERS_SENT |
router.get("/menu", ...) mounted at /menu | Write the full path inside the router | Paths inside are relative to the mount | The route lives at /menu/menu |
Forgetting app.use(router) | Creating a router is enough | It must be mounted | Every route gives 404 |
No mergeParams on a child router | Child routers see all params | They only see their own | req.params.id is undefined |
import menu from "./routes/menu" | Node.js adds .js | ES modules need the full name | ERR_MODULE_NOT_FOUND |
| Repeating the same lookup in every route | It is just a few lines | router.param does it once | Bugs when one copy changes |
Check Yourself
Exercises
What to Remember
- A route is a method, a path and a handler. The first match wins.
:namecaptures path parts intoreq.params, always as strings.- Express 5 uses
/*splatfor wildcards and{/:id}for optional parts. - Query values are strings. Convert them and give safe defaults.
- Specific routes first, catch-all last.
express.Router()makes a mini app for related routes.- Mount it with
app.use(path, router). Paths inside are relative. - Child routers need
mergeParams: trueto read parent params. router.paramloads data once for every route with that param.
Next: Middleware Fundamentals. You will learn the (req, res, next) functions that run before your routes.