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.
Setup: npm install then node hello-express.js
Try it live: cse135.site:3002/demo - An Express server running on this site
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.
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 dependenciesnode_modules/ - Installed packages (don't commit to git)package-lock.json - Exact versions (do commit)// 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.
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}`);
});
// :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}`);
});
// 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}`);
});
Express provides convenient ways to access all parts of a request:
// 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 });
});
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 } });
});
req.body is undefined, you forgot to add the body-parsing middleware, or you added it AFTER your routes. Middleware order matters!
| 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 |
Express provides convenient response methods:
// Send plain text or HTML
res.send('Hello');
res.send('<h1>Hello</h1>');
// 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');
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.csspublic/images/logo.png → http://localhost:3000/images/logo.pngMiddleware functions run before your route handlers. They can:
// 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 }));
next() function: Middleware must call next() to pass control to the next handler, or send a response. If you forget, the request will hang.
In Express, order matters. Middleware and routes are processed in the order they're defined. This is a common source of bugs.
// 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!
});
// 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
});
// 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
});
app.use((req, res, next) => { console.log(req.method, req.url); next(); });
// 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('<h1>Welcome</h1><a href="/api/items">View Items</a>');
});
// 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);
We have an Express server running on this site. Try the interactive demo to see headers, query strings, route parameters, and request bodies in action:
This demo lets you:
req.get()req.queryreq.paramsreq.bodyView the source: express-demo.js
app.get(), app.post(), etc.:param for route parameters, req.query for query stringsreq.get('header-name') or req.headersreq.body (requires express.json() or express.urlencoded() middleware)res.send(), res.json(), res.redirect()app.use(express.static('folder'))