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.

  1. A sign at the gate says: Library, Room 1. Lab, Room 2.
  2. A visitor reads the signs from top to bottom.
  3. They walk to the first room that matches.
  4. 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:

  1. Route methods, paths and parameters.
  2. The Express 5 path syntax: named wildcards and optional parts.
  3. Query strings and safe value conversion.
  4. Route order, handler chains, next("route") and app.route().
  5. How to move routes into files with express.Router().
  6. Router middleware, nested routers, mergeParams and router.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.all for 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/:id gives two values.
  • Regular expression route: app.get(/^\/item-\d+$/, ...) matches by pattern.
  • /menu/3 sets req.params.id to "3".
  • /menu/snacks/12 sets both category and id.
  • 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: /*splat matches any path. req.params.splat is 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.pdf gives the parts ["notes", "term1.pdf"].
  • /orders and /orders/9 use 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=spicy gives an array ["veg", "spicy"].
  • Missing name: req.query.page is undefined.
  • 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 with Number.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=abc gives 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/7 is 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/today must come before /menu/:id.
  • Otherwise :id catches the word "today".
  • /menu/today answers "Item today". That is the bug.
  • Move the /menu/today route above /menu/:id and 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/5 runs all three handlers of the first route.
  • /orders/0 jumps to the second route with next("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 /orders gives 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.get and app.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.

  1. The gate guard sends science questions to the science desk.
  2. Sports questions go to the sports desk.
  3. Each desk handles its own questions in its own way.
  4. 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 = /Menu and /menu are different.
  • strict: true = /menu and /menu/ are different.
  • mergeParams: true = lets a child router read the parent's params.
  • Requests to /orders print a log line.
  • /menu prints 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, itemId is "2" or "3".
  • Without it, itemId becomes "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 :id then gets a ready req.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/99 stops 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

MistakeWrong ideaCorrect ideaWhat goes wrong
/menu/:id above /menu/todayExpress picks the best matchExpress picks the first matchtoday is read as an id
Catch-all route at the topIt only catches leftoversIt catches everything below itEvery request gets 404
app.get("*", ...) in Express 5Old wildcards still workUse "/*splat"TypeError: Missing parameter name
if (req.query.veg) for ?veg=false"false" is falseAny non-empty string is truthyNon-veg items are hidden
req.query.page * limit without checksQuery values are numbersThey are strings, maybe junkNaN and empty pages
Calling next() twiceIt is harmlessTwo handlers run, two responsesERR_HTTP_HEADERS_SENT
router.get("/menu", ...) mounted at /menuWrite the full path inside the routerPaths inside are relative to the mountThe route lives at /menu/menu
Forgetting app.use(router)Creating a router is enoughIt must be mountedEvery route gives 404
No mergeParams on a child routerChild routers see all paramsThey only see their ownreq.params.id is undefined
import menu from "./routes/menu"Node.js adds .jsES modules need the full nameERR_MODULE_NOT_FOUND
Repeating the same lookup in every routeIt is just a few linesrouter.param does it onceBugs when one copy changes

Check Yourself

Exercises

What to Remember

  • A route is a method, a path and a handler. The first match wins.
  • :name captures path parts into req.params, always as strings.
  • Express 5 uses /*splat for 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: true to read parent params.
  • router.param loads data once for every route with that param.

Next: Middleware Fundamentals. You will learn the (req, res, next) functions that run before your routes.