Learning Objectives
Set up an Express project using npm and install dependencies
Define routes for different HTTP methods using app.get(), app.post(), etc.
Access route parameters, query strings, headers, and request bodies
Serve static files with express.static()
Understand middleware, the next() function, and why ordering matters
Use Express response methods: res.send(), res.json(), res.redirect()
Why Express?
Express is the most popular Node.js web framework. It provides routing, middleware, and utilities that make web development much easier than using raw http.
Compare Module 02's routing code to Express:
Raw Node.js (http module)
Express
if (req.url === '/about' &&
req.method === 'GET') {
res.writeHead(200, {...});
res.end('About page');
}
app.get('/about', (req, res) => {
res.send('About page');
});
Express handles URL parsing, headers, and much more automatically.
Getting Started with npm
Express is installed via npm (Node Package Manager):
# Create a new project
mkdir my-app
cd my-app
# Initialize package.json
npm init -y
# Install Express
npm install express
This creates:
package.json — Project metadata and dependencies
node_modules/ — Installed packages (don't commit to git)
package-lock.json — Exact versions (do commit)
Hello Express
// hello-express.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
That's it! No manual URL parsing, no header management, no content-type setting.
Routing
Express provides methods for each HTTP verb:
// GET request
app.get('/users', (req, res) => {
res.send('Get all users');
});
// POST request
app.post('/users', (req, res) => {
res.send('Create a user');
});
// PUT request
app.put('/users/:id', (req, res) => {
res.send(`Update user ${req.params.id}`);
});
// DELETE request
app.delete('/users/:id', (req, res) => {
res.send(`Delete user ${req.params.id}`);
});
Route Parameters
// :id is a parameter
app.get('/users/:id', (req, res) => {
const userId = req.params.id; // Access the parameter
res.send(`User ID: ${userId}`);
});
// Multiple parameters
app.get('/posts/:year/:month', (req, res) => {
const { year, month } = req.params;
res.send(`Posts from ${month}/${year}`);
});
Query Strings
// URL: /search?q=nodejs&page=2
app.get('/search', (req, res) => {
const query = req.query.q; // 'nodejs'
const page = req.query.page; // '2'
res.send(`Searching for: ${query}, page ${page}`);
});
Reading Request Data
Express provides convenient ways to access all parts of a request:
Headers
// Express provides req.get() as a shortcut
app.get('/info', (req, res) => {
const userAgent = req.get('user-agent'); // Cleaner than req.headers['user-agent']
const contentType = req.get('content-type');
const host = req.get('host');
// Client IP (handles proxies)
const clientIP = req.ip; // or req.headers['x-forwarded-for']
res.json({ userAgent, contentType, host, clientIP });
});
Request Body
To access POST data in req.body, you must enable the parsing middleware first:
// IMPORTANT: Add these BEFORE your routes!
app.use(express.json()); // For JSON bodies
app.use(express.urlencoded({ extended: true })); // For form bodies
app.post('/login', (req, res) => {
// Now req.body contains the parsed data
const { username, password } = req.body;
res.json({ received: { username, password } });
});
Common mistake: If req.body is undefined, you forgot to add the body-parsing middleware, or you added it AFTER your routes. Middleware order matters!
Comparison: Raw Node.js vs Express
Task
Raw Node.js
Express
Query string
url.parse(req.url, true).query
req.query
Route parameters
Manual URL parsing
req.params
Headers
req.headers['user-agent']
req.get('user-agent')
POST body
Collect stream chunks manually
req.body (with middleware)
Client IP
req.connection.remoteAddress
req.ip
Response Methods
Express provides convenient response methods:
// Send plain text or HTML
res.send('Hello');
res.send('Hello ');
// Send JSON (sets Content-Type automatically)
res.json({ name: 'Alice', age: 25 });
// Send a file
res.sendFile('/path/to/file.html');
// Set status code
res.status(404).send('Not found');
// Redirect
res.redirect('/other-page');
res.redirect(301, '/permanent-redirect');
Serving Static Files
Remember the complex static file code from Module 02? Express does it in one line:
// Serve files from the 'public' directory
app.use(express.static('public'));
Now files in public/ are accessible:
public/style.css → http://localhost:3000/style.css
public/images/logo.png → http://localhost:3000/images/logo.png
Middleware
Middleware functions run before your route handlers. They can:
Log requests
Parse request bodies
Check authentication
Modify request/response objects
// Middleware runs for ALL requests
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Continue to the next handler
});
// Built-in middleware for parsing JSON
app.use(express.json());
// Built-in middleware for parsing form data
app.use(express.urlencoded({ extended: true }));
The next() function: Middleware must call next() to pass control to the next handler, or send a response. If you forget, the request will hang.
Middleware and Route Ordering
In Express, order matters . Middleware and routes are processed in the order they're defined. This is a common source of bugs.
Middleware Must Come Before Routes
// WRONG - body parser comes after route
app.post('/data', (req, res) => {
console.log(req.body); // undefined!
});
app.use(express.json()); // Too late!
// CORRECT - body parser comes first
app.use(express.json()); // Parse JSON bodies
app.post('/data', (req, res) => {
console.log(req.body); // Works!
});
Route Order Matters
// WRONG - catch-all route comes first
app.get('/users/*', (req, res) => {
res.send('Catch all');
});
app.get('/users/profile', (req, res) => {
res.send('Profile page'); // Never reached!
});
// CORRECT - specific routes first
app.get('/users/profile', (req, res) => {
res.send('Profile page'); // Matches first
});
app.get('/users/*', (req, res) => {
res.send('Catch all'); // Only if above didn't match
});
404 Handler Must Be Last
// WRONG - 404 handler catches everything
app.use((req, res) => {
res.status(404).send('Not found');
});
app.get('/about', (req, res) => {
res.send('About'); // Never reached!
});
// CORRECT - 404 at the very end
app.get('/about', (req, res) => {
res.send('About');
});
// ... all other routes ...
app.use((req, res) => {
res.status(404).send('Not found'); // Only unmatched requests reach here
});
Debugging tip: If a route isn't working, check if something defined earlier is catching the request. Add logging middleware at the top to see what's happening: app.use((req, res, next) => { console.log(req.method, req.url); next(); });
Complete Example
// routes.js - Complete Express example
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(express.static('public'));
// Routes
app.get('/', (req, res) => {
res.send('Welcome View Items ');
});
// API routes
app.get('/api/items', (req, res) => {
res.json([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
]);
});
app.get('/api/items/:id', (req, res) => {
res.json({ id: req.params.id, name: `Item ${req.params.id}` });
});
app.post('/api/items', (req, res) => {
const newItem = req.body;
res.status(201).json({ message: 'Created', item: newItem });
});
// 404 handler (must be last)
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
app.listen(3000);
Summary
Express simplifies routing with app.get(), app.post(), etc.
Use :param for route parameters, req.query for query strings
Access headers with req.get('header-name') or req.headers
Access POST data with req.body (requires express.json() or express.urlencoded() middleware)
Response methods: res.send(), res.json(), res.redirect()
Static files: app.use(express.static('folder'))
Middleware processes requests before route handlers
Order matters: Middleware before routes, specific routes before catch-alls, 404 handler last
Key Takeaways
Express drastically reduces boilerplate by providing clean routing methods, automatic content-type handling, and built-in body parsing.
Route parameters (:id) and req.query replace the manual URL parsing required with raw Node.js.
Middleware functions form a processing pipeline — they run in order before route handlers and must call next() to continue.
Order is critical in Express: body-parsing middleware must precede routes, specific routes must precede catch-alls, and the 404 handler must be last.
express.static() replaces dozens of lines of manual file-serving code with a single line.