Module 9 — Web Servers with Node.js and Express¶
Focus: Building Web Servers with Node.js and Express.js
Learning Outcomes
By the end of this module, you will be able to:
- Understand the purpose of Express.js in web development.
- Create a simple Express web server.
- Handle different HTTP routes and requests.
- Serve static files (HTML, CSS, and JS) using Express.
- Use basic middleware to process and manage requests.
9.0 Overview¶
In the previous module, you learned how to run JavaScript outside of the browser using Node.js. Now, we’ll expand that knowledge to build web servers using Node.js and its most popular framework, Express.js.
Express simplifies the process of handling web requests, managing routes, and sending responses compared to Node’s built-in http module.
9.1 Client-Side vs. Server-Side Development¶
Web development can be divided into two main categories:
- Client-Side Development: Code that runs in the browser (HTML, CSS, and JavaScript) to control layout and interactivity.
- Server-Side Development: Code that runs on the server, handling requests, accessing databases, and sending responses back to the client.
When a user visits a website, the browser (client) sends a request to the server, which processes it and returns a response — usually an HTML page, JSON data, or a file.
9.2 Node.js Server Example¶
Node.js includes a built-in module called http that lets you create a basic web server. However, writing larger applications using only this module can quickly become complex.
Example: Using the http Module (ES Modules)¶
import http from 'http';
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Welcome to the Home Page!');
} else if (req.url === '/about') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('This is the About Page.');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Page Not Found');
}
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
This code creates a Node.js server that handles two routes (/ and /about) and returns a 404 message for all others. You can test it by visiting:
http://localhost:3000— displays Welcome to the Home Page!http://localhost:3000/about— displays This is the About Page.
While this approach works, it becomes inefficient for larger applications that require multiple routes and more advanced logic.
9.3 Framework and Express.js¶
A framework is a reusable, structured collection of libraries, tools, and conventions that provide a foundation for building applications. It defines how code is organized and offers built‑in features (like routing, middleware, and error handling) so developers don’t have to create everything from scratch.
Express.js is a fast, minimalist web framework for Node.js. It provides a simple interface for building web servers and APIs by simplifying common tasks such as routing, handling requests, and managing responses.
Key Features¶
- Simplified Routing: Define URL paths and their responses with concise syntax.
- Middleware Support: Easily add functions that process requests before sending responses (e.g., for logging or authentication).
- Static File Serving: Serve HTML, CSS, JavaScript, and images directly with one line of code.
- Integration-Friendly: Works seamlessly with databases, templating engines, and RESTful APIs.
Express saves developers from manually managing low-level server logic and helps organize applications into clear, modular components. It’s the foundation for most Node.js web applications.
9.4 Setting Up Express in VS Code¶
Express.js simplifies server creation. Follow these steps to get started:
- Create a New Project Folder
In VS Code, create a folder, for example:C:\Users\YourName\Desktop\express-demo -
Initialize the Project
Open the terminal and run:This creates anpm init -ypackage.jsonfile to manage your project’s dependencies. -
Enable ES Modules
Openpackage.jsonand add the following property at the top level:{ "type": "module" }Reminder
This allows Node.js to use
importandexportsyntax. -
Install Express.js
Run:npm install expressNote
You need to install Express.js once per project. Each project folder has its own
node_modulesdirectory. If you create a new project, you’ll need to reinstall it within that folder. -
Create
server(ES Modules)
Inside your folder, create a file namedserver.jsand add:import express from 'express'; const app = express(); app.get('/', (req, res) => { res.send('Welcome to my first Express server!'); }); app.listen(3000, () => { console.log('Server running at http://localhost:3000'); }); -
Run the Server
In the terminal, type:Then open your browser and go tonode server.jshttp://localhost:3000.
9.5 Routing in Express¶
A route defines how your server responds to a request for a specific URL or HTTP method.
Example:
app.get('/', (req, res) => {
res.send('Home Page');
});
app.get('/about', (req, res) => {
res.send('About Page');
});
app.get('/contact', (req, res) => {
res.send('Contact Page');
});
Now your server can return different responses depending on the requested route.
9.6 Serving Static Files¶
Express can easily serve static files such as HTML, CSS, images, and client-side JavaScript.
-
Create a Folder Named
public
This folder stores all your static files (HTML, CSS, JavaScript, and images). -
Add Sample Files
For example:index.htmlstyle.cssscript.js
-
Tell Express to Serve the Folder
Add this line to yourserver.js:app.use(express.static('public'));How It Works
When someone visits
http://localhost:3000/style.css, Express automatically looks for a file namedstyle.cssinside thepublicfolder and sends it to the browser. -
Access the Files in Your Browser
http://localhost:3000/— loadspublic/index.htmlhttp://localhost:3000/style.css— loads your stylesheethttp://localhost:3000/script.js— loads your script
Tip
This setup keeps front-end files organized while your Express code focuses on backend logic.
9.7 Middleware Basics¶
Middleware are functions that execute between receiving a request and sending a response. They are powerful tools for logging, authentication, and data parsing.
Example:
app.use((req, res, next) => {
console.log(`${req.method} request for '${req.url}'`);
next(); // Move to the next middleware or route handler
});
Callback Parameters
The callback in app.get() usually takes two parameters — req (the request object) and res (the response object). You can include a third parameter, next, to pass control to the next middleware or route when chaining functions.
Common built-in middleware includes:
express.static()— serves static files.express.json()— parses incoming JSON data.
9.7.1 Writing Two Custom Middleware Functions for a Specific Route¶
import express from 'express';
const app = express();
// Middleware #1 — simple logger
function loggerMiddleware(req, res, next) {
console.log(`${req.method} ${req.url}`);
next();
}
// Middleware #2 — attach a timestamp
function timestampMiddleware(req, res, next) {
req.requestTime = new Date().toISOString();
next();
}
// Using both on a single route
app.get('/hello', loggerMiddleware, timestampMiddleware, (req, res) => {
res.send(`Hello! Request received at ${req.requestTime}`);
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
What happens?
- A request arrives for
/hello. loggerMiddlewareruns first and callsnext()function in the pipeline.timestampMiddlewareruns next and callsnext()function in the pipeline.- The final route handler sends the response.
Note
Execution order matters. Middlewares run left‑to‑right as listed in the route (or top‑to‑bottom when registered globally with app.use).
9.7.2 Applying Middleware Globally (All Routes)¶
You need to register your middleware functions with app.use() so every request goes through these functions:
import express from 'express';
const app = express();
function loggerMiddleware(req, res, next) {
console.log(`${req.method} ${req.url}`);
next();
}
function timestampMiddleware(req, res, next) {
req.requestTime = new Date().toISOString();
next();
}
// Global registration — order matters
app.use(loggerMiddleware);
app.use(timestampMiddleware);
app.get('/', (req, res) => {
res.send(`Welcome! (at ${req.requestTime})`);
});
app.get('/hello', (req, res) => {
res.send(`Hello there! (at ${req.requestTime})`);
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
Warning
If you forget to call next() (and you do not end the response), the request will hang.
9.7.3 Limiting Middleware to a Path Prefix (Scoped)¶
Use a path argument with app.use() to scope the middleware to routes that begin with that prefix:
import express from 'express';
const app = express();
function loggerMiddleware(req, res, next) {
console.log(`API request: ${req.method} ${req.url}`);
next();
}
function timestampMiddleware(req, res, next) {
req.requestTime = new Date().toISOString();
next();
}
// Only apply to routes starting with "/api"
app.use('/api', loggerMiddleware, timestampMiddleware);
app.get('/', (req, res) => {
res.send('Home page — no scoped middleware here');
});
app.get('/api/users', (req, res) => {
res.send(`User list (requested at ${req.requestTime})`);
});
app.get('/api/products', (req, res) => {
res.send(`Product list (requested at ${req.requestTime})`);
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
Result: Requests to /api/* pass through both middlewares; other routes skip them.
9.8 Summary¶
In this module, you learned how to:
- Set up a project using Express.js.
- Create and manage routes.
- Serve static files efficiently.
- Use middleware to process requests.
Express is the backbone of many modern web applications built with Node.js. In the next module, you’ll learn how to create dynamic pages using templating engines and handle form submissions.