Routing
Routing
Level 7 — Web Servers & APIs The mechanism of defining how an application responds to a client request to a specific Endpoint (URL path) and a specific HTTP Request Method (GET, POST, PUT, DELETE).
1. Prerequisites
- Express.js — The framework that makes Routing clean and manageable.
- REST API Design — The design philosophy that dictates how routes should be named.
2. Term Category
API Architecture (Web Servers): Routing is a fundamental concept in this technology stack. Level 7 — Web Servers & APIs
3. Explanation
(1) Design Motivation — "Why did we design this?"
If a server only had one job (like returning the current time), you wouldn't need routing. Any request to the server would return the time. But modern applications are massive. A single server handles registering users, logging in, fetching products, creating orders, and deleting comments. Routing is the system of organizing the server like a massive office building with thousands of doors. The router looks at the URL (the door number) and the Method (the type of knock), and directs the user to the correct piece of code inside the server.
(2) The Two Halves of a Route
A Route is defined by a combination of two things:
- The HTTP Method: Represents the Action (GET = Read, POST = Create, DELETE = Destroy).
- The Path (URL): Represents the Resource (
/users,/products/12).
// Route 1: Read all users
app.get('/users', (req, res) => { /* ... */ });
// Route 2: Create a new user
app.post('/users', (req, res) => { /* ... */ });
// Route 3: Delete a specific user
app.delete('/users/12', (req, res) => { /* ... */ });
Notice that Route 1 and Route 2 have the exact same Path (/users), but because the Methods are different (GET vs POST), the Router treats them as completely separate doors!
(3) The Express Router
If you put 500 routes inside server.js, the file becomes an unreadable nightmare.
Express provides the express.Router() class, which allows you to split your routes into separate files. You can create a userRoutes.js file, define all the user routes, and import them into server.js as one clean block.
4. Common Mistakes & Pitfalls
Mistake 1: Route Order (The "Catch-All" Trap)
The mistake: A developer writes a dynamic route for fetching users by ID, and then writes a route for the admin panel:
app.get('/users/:id', getUserById);
app.get('/users/admin', getAdminPanel);
When they visit /users/admin, the server crashes trying to look up a user with the ID of "admin".
Why it's wrong: Express reads routes top-to-bottom. When the request for /users/admin arrives, it hits the :id route first. Express thinks "admin" is a dynamic ID, triggers getUserById("admin"), and never reaches the actual admin panel route!
Golden Rule: Always put your specific, static routes (/users/admin) ABOVE your generic, dynamic routes (/users/:id).
Mistake 2: Placing Catch-All 404 Route Handlers Above Specific API Routes
The mistake: Mounting app.use('*', notFoundHandler) at top of script file.
Why it's wrong: Express evaluates routes top-to-bottom. Placing a wildcard 404 handler at the top intercepts all incoming HTTP requests before specific API handlers are evaluated.
Incorrect:
app.use('*', (req, res) => res.status(404).send('Not Found')); // ❌ Intercepts all routes!
app.get('/api/users', ...);
Fix:
app.get('/api/users', ...); // Specific routes first
app.use('*', (req, res) => res.status(404).send('Not Found')); // 404 fallback LAST
Mistake 3: Failing to Modularize Routes via express.Router() in Large Apps
The mistake: Writing 500 route handlers directly on a single global app object inside server.js.
Why it's wrong: Monolithic route files become unmaintainable. Group related resource routes using express.Router() into modular controller files.
Incorrect:
// 500 lines of app.get(), app.post() in server.js
Fix:
// routes/users.js:
const router = express.Router();
router.get('/', getUsers);
module.exports = router;
// server.js:
app.use('/users', userRouter);
5. Practice Exercises
Exercise 1: Express Router Modular Factory
Scenario: Constructs a modular RESTful resource router for /users containing CRUD route handlers.
Requirements:
- Write createResourceRouter(controllerObj).
- Bind GET, POST, PUT, DELETE endpoints.
- Return routing table.
Answer
Implementation
function createResourceRouter(controllerObj = {}) {
const routes = [];
const register = (method, path, handlerName) => {
if (typeof controllerObj[handlerName] === "function") {
routes.push({ method, path, handler: controllerObj[handlerName] });
}
};
register("GET", "/", "list");
register("POST", "/", "create");
register("GET", "/:id", "getById");
register("PUT", "/:id", "update");
register("DELETE", "/:id", "delete");
return {
routes,
routeCount: routes.length
};
}
// Verification tests
const controller = {
list: () => {},
create: () => {},
getById: () => {},
update: () => {},
delete: () => {}
};
const router = createResourceRouter(controller);
console.assert(router.routeCount === 5, "Test 1 Failed: All 5 CRUD routes registered");
Technical Explanation
- RESTful Routing Conventions: Maps standard HTTP verbs (GET, POST, PUT, DELETE) to resource actions.
- Separation of Concerns: Separates URL routing declarations from business logic controller implementations.
- Modular Router Export: Modular routers are exported as standalone CommonJS/ESM modules.
Exercise 2: Regex & Wildcard Route Matcher
Scenario: Simulates Express wildcard (/files/*) and regex route matching algorithm.
Requirements:
- Write matchWildcardRoute(routePattern, reqPath).
- Support
*wildcard matching. - Return match result.
Answer
Implementation
function matchWildcardRoute(routePattern, reqPath) {
if (routePattern === "*") {
return { matched: true, wildcardValue: reqPath };
}
const regexPattern = routePattern.replace(/\*/g, "(.*)");
const regex = new RegExp(`^${regexPattern}$`);
const match = reqPath.match(regex);
if (!match) {
return { matched: false, wildcardValue: null };
}
return {
matched: true,
wildcardValue: match[1] || ""
};
}
// Verification tests
const r1 = matchWildcardRoute("/static/*", "/static/images/logo.png");
console.assert(r1.matched === true, "Test 1 Failed");
console.assert(r1.wildcardValue === "images/logo.png", "Test 2 Failed: Captured wildcard subpath");
Technical Explanation
- Wildcard Routes: Asterisks (
*) match arbitrary path segments in route definitions. - Catch-All Fallback Routes: Wildcard
app.use('*')routes registered at the end of the stack handle 404 pages. - Regex Route Support: Express accepts JavaScript Regular Expressions directly as route path arguments.
Exercise 3: RESTful Resource Routing Table
Scenario: Executes matching route handlers from a pre-compiled routing table for incoming HTTP requests.
Requirements:
- Write dispatchRoutingTable(method, path, routingTable).
- Match method and path.
- Execute handler.
Answer
Implementation
function dispatchRoutingTable(method, path, routingTable = []) {
const targetMethod = method.toUpperCase();
for (const route of routingTable) {
if (route.method === targetMethod && route.path === path) {
return { matched: true, response: route.handler() };
}
}
return { matched: false, response: null };
}
// Verification tests
const table = [
{ method: "GET", path: "/health", handler: () => "OK" }
];
const res = dispatchRoutingTable("GET", "/health", table);
console.assert(res.matched === true && res.response === "OK", "Test 1 Failed");
Technical Explanation
- Routing Table Execution: Internal data structure mapping HTTP method and path combinations to handler functions.
- Routing Priority: Routes are evaluated in top-to-bottom registration order; first matching route executes.
- Overlapping Route Ordering: Specific routes (
/users/active) MUST be defined before parameter routes (/users/:id).
6. Related Terms
- Middleware — Middleware executes right before the Router triggers your code.
- REST API Design — The strict rules for naming your routes properly.
- Express.js — Related concept: Express.js.
- The req & res Objects — Related concept: The req & res Objects.
- Route Parameters & Query Strings — Related concept: Route Parameters & Query Strings.
- Serving Static Files (express.static) — Related concept: Serving Static Files (express.static).
7. Key Takeaways
- Routing directs incoming requests to the correct block of code.
- A Route is a unique combination of an HTTP Method (GET/POST) and a Path (
/users). - Routes are evaluated Top-to-Bottom. Always put specific routes above generic/dynamic routes.
- Use
express.Router()to split massive applications into clean, separate files.