Express.js Fundamentals & Routing

📖 Concept

Express.js is a minimal, unopinionated web framework for Node.js. It sits on top of the built-in http module and provides a clean API for routing, middleware, and request/response enhancement.

Why Express?

  • 30K+ GitHub stars, most downloaded Node.js framework (~30M weekly npm downloads)
  • Minimal core — extend with middleware for exactly what you need
  • Huge ecosystem of middleware packages
  • Used by Uber, IBM, PayPal, Twitter

Core concepts:

  1. Applicationconst app = express() — your Express application
  2. Routes — Map URL patterns + HTTP methods to handler functions
  3. Middleware — Functions that process requests before they reach route handlers
  4. Request (req) — Enhanced IncomingMessage with parsed body, params, query
  5. Response (res) — Enhanced ServerResponse with .json(), .send(), .status()

Routing patterns:

app.get('/users', handler)         // GET /users
app.post('/users', handler)        // POST /users
app.put('/users/:id', handler)     // PUT /users/123
app.delete('/users/:id', handler)  // DELETE /users/123
app.all('/api/*', handler)         // Any method, any path under /api/
app.use('/admin', adminRouter)     // Mount sub-router

Route parameters:

// :id captures the value as req.params.id
app.get('/users/:id/posts/:postId', (req, res) => {
  const { id, postId } = req.params;  // { id: '42', postId: '7' }
});

Express added features over raw http:

Feature Raw http Express
Routing Manual if/else app.get(), app.post(), Router
Body parsing Manual stream collection express.json() middleware
Query params Manual URL parsing req.query (auto-parsed)
Static files Manual MIME + streaming express.static()
JSON response Manual JSON.stringify + headers res.json()

🏠 Real-world analogy: If the raw http module is building a house from scratch (foundation, framing, plumbing), Express is like a pre-fabricated house kit — the structure is ready, you just customize the rooms (routes) and add furniture (middleware).

💻 Code Example

codeTap to expand ⛶
1// Express.js — Complete API Server
2
3const express = require("express");
4const app = express();
5
6// Built-in middleware
7app.use(express.json({ limit: "10mb" }));
8app.use(express.urlencoded({ extended: true }));
9app.use(express.static("public"));
10
11// In-memory data store (use a database in production!)
12let users = [
13 { id: 1, name: "Alice", email: "alice@example.com", role: "admin" },
14 { id: 2, name: "Bob", email: "bob@example.com", role: "user" },
15 { id: 3, name: "Charlie", email: "charlie@example.com", role: "user" },
16];
17let nextId = 4;
18
19// === ROUTES ===
20
21// GET /api/users — List all users (with query filters)
22app.get("/api/users", (req, res) => {
23 let result = [...users];
24
25 // Filter by role
26 if (req.query.role) {
27 result = result.filter((u) => u.role === req.query.role);
28 }
29
30 // Search by name
31 if (req.query.search) {
32 const search = req.query.search.toLowerCase();
33 result = result.filter((u) => u.name.toLowerCase().includes(search));
34 }
35
36 // Pagination
37 const page = parseInt(req.query.page) || 1;
38 const limit = parseInt(req.query.limit) || 10;
39 const start = (page - 1) * limit;
40 const paginated = result.slice(start, start + limit);
41
42 res.json({
43 data: paginated,
44 meta: {
45 total: result.length,
46 page,
47 limit,
48 totalPages: Math.ceil(result.length / limit),
49 },
50 });
51});
52
53// GET /api/users/:id — Get a specific user
54app.get("/api/users/:id", (req, res) => {
55 const id = parseInt(req.params.id);
56 const user = users.find((u) => u.id === id);
57
58 if (!user) {
59 return res.status(404).json({ error: "User not found" });
60 }
61
62 res.json({ data: user });
63});
64
65// POST /api/users — Create a new user
66app.post("/api/users", (req, res) => {
67 const { name, email, role } = req.body;
68
69 // Validation
70 if (!name || !email) {
71 return res.status(400).json({
72 error: "Validation failed",
73 details: {
74 ...(!name && { name: "Name is required" }),
75 ...(!email && { email: "Email is required" }),
76 },
77 });
78 }
79
80 // Check duplicate email
81 if (users.some((u) => u.email === email)) {
82 return res.status(409).json({ error: "Email already exists" });
83 }
84
85 const newUser = { id: nextId++, name, email, role: role || "user" };
86 users.push(newUser);
87
88 res.status(201).json({ data: newUser });
89});
90
91// PUT /api/users/:id — Update a user
92app.put("/api/users/:id", (req, res) => {
93 const id = parseInt(req.params.id);
94 const index = users.findIndex((u) => u.id === id);
95
96 if (index === -1) {
97 return res.status(404).json({ error: "User not found" });
98 }
99
100 users[index] = { ...users[index], ...req.body, id }; // Don't allow ID change
101 res.json({ data: users[index] });
102});
103
104// DELETE /api/users/:id — Delete a user
105app.delete("/api/users/:id", (req, res) => {
106 const id = parseInt(req.params.id);
107 const index = users.findIndex((u) => u.id === id);
108
109 if (index === -1) {
110 return res.status(404).json({ error: "User not found" });
111 }
112
113 users.splice(index, 1);
114 res.status(204).end();
115});
116
117// Health check
118app.get("/health", (req, res) => {
119 res.json({ status: "healthy", uptime: process.uptime() });
120});
121
122// 404 handler (must come after all routes)
123app.use((req, res) => {
124 res.status(404).json({ error: `Route ${req.method} ${req.path} not found` });
125});
126
127// Start server
128const PORT = process.env.PORT || 3000;
129app.listen(PORT, () => {
130 console.log(`Express server running on http://localhost:${PORT}`);
131});

🏋️ Practice Exercise

Exercises:

  1. Build a complete CRUD REST API for a "todos" resource with Express
  2. Add query parameter support: filtering, searching, sorting, and pagination
  3. Implement route parameter validation (check that :id is a valid integer)
  4. Create a Router module that separates user routes into their own file
  5. Add a catch-all 404 handler and test it with invalid routes
  6. Build a simple HTML form that submits data to your Express API using express.urlencoded()

⚠️ Common Mistakes

  • Not calling next() in middleware — the request hangs forever because Express doesn't know to proceed to the next handler

  • Defining error-handling middleware before routes — Express middleware runs in order; put error handlers AFTER all routes

  • Sending multiple responses — calling res.json() or res.send() more than once throws an error; use return after sending

  • Using app.listen() in the same file as app creation — this makes testing impossible; export app separately from the listen call

  • Not using express.json() middleware — without it, req.body is undefined for JSON POST/PUT requests

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Express.js Fundamentals & Routing. Login to unlock this feature.