Express is a minimal and flexible web application framework for Node.js. It provides a robust set of features for building web and mobile applications, with a focus on simplicity and performance.
Understanding Express fundamentals is essential for building backend applications. This includes the application object, middleware system, routing, and how requests flow through an Express application.
The Application Object
The Express application object is created by calling the top-level express function. It has methods for routing HTTP requests, configuring middleware, rendering HTML views, and registering template engines.
To start an Express application, you require the module and invoke it:
const express = require('express');
const app = express();
The app object is the primary interface for configuring your Express application. It provides methods for setting configuration, mounting middleware, defining routes, and starting the server.
Application Settings
You can control application behavior using set, enable, and disable methods. app.set assigns a setting to a value. app.get returns the value of a setting. app.enable and app.disable set boolean settings.
The app.locals object has properties that are local variables within the application. Once set, these persist throughout the life of the application and are available to all views and middleware.
Middleware
Middleware functions are functions that have access to the request object, the response object, and the next middleware function in the application’s request-response cycle.
Middleware Stack (Onion Model):
Request
↓
┌─────────────────────┐
│ Middleware 1 │
│ (Security Headers) │
└──────────┬───────────┘
↓
┌─────────────────────┐
│ Middleware 2 │
│ (Body Parser) │
└──────────┬───────────┘
↓
┌─────────────────────┐
│ Middleware 3 │
│ (Authentication) │
└──────────┬───────────┘
↓
┌─────────────────────┐
│ Route Handler │
│ (Controller) │
└──────────┬───────────┘
↓
Response
Middleware can execute code, make changes to the request and response objects, end the request-response cycle, and call the next middleware function in the stack.
The app.use method mounts middleware functions at a specified path. If no path is specified, it defaults to the root path, meaning the middleware will be executed for every request to the app.
Example: Middleware Chain
app.use((req, res, next) => {
console.log('Middleware 1: Request received');
req.timestamp = Date.now();
next(); // Pass control to next middleware
});
app.use((req, res, next) => {
console.log('Middleware 2: Processing');
next();
});
app.get('/api/data', (req, res) => {
console.log('Route handler: Sending response');
res.json({ data: 'Hello', timestamp: req.timestamp });
});
Built-in Middleware
Express includes several built-in middleware functions. express.static serves static files from a specified root directory. express.json parses incoming requests with JSON payloads. express.urlencoded parses incoming requests with URL-encoded payloads.
Security note: since req.body is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting.
Router-Level Middleware
A router object is an instance of middleware and routes, often referred to as a mini-application. You can use router.use to load middleware specific to that router.
Middleware applied to a router will run for all requests on that router’s path. The order in which you define middleware with router.use is very important as they are invoked sequentially.
Routing
Routing refers to how an application’s endpoints respond to client requests. You can define routing using methods of the Express app object or the Router object.
Express supports routing methods corresponding to HTTP verbs: app.get, app.post, app.put, app.delete, and others. app.all matches all HTTP verbs and is useful for mapping global logic for specific path prefixes.
Route paths can be strings, string patterns with special characters, or regular expressions. The path determines which requests invoke the middleware or handler.
The Router Object
A Router object is an isolated instance of middleware and routes. You can use express.Router to create a new router object, define routes on it, and then mount it on a path in the main app.
This enables modular routing, where you can organize routes into separate files and mount them at different paths. This is essential for building maintainable applications.
Chainable Route Handlers
app.route returns a single route instance, allowing you to chain multiple HTTP verb handlers to the same path. This avoids redundancy and reduces typos by keeping logic for a single resource grouped together.
Response Methods
The methods on the res object send the response to the client and terminate the request-response cycle. res.send sends the HTTP response with a string, object, or buffer. res.json sends a JSON response. res.redirect redirects to a URL. res.render renders a view template. res.end ends the response process without any data.
Error-Handling Middleware
Error-handling middleware is defined with four arguments: err, req, res, and next. You must provide four arguments to identify a function as error-handling middleware. Even if you don’t need to use the next object, you must specify it to maintain the signature.
Execution Order
Middleware functions are executed sequentially. The order of middleware inclusion is critical. Middleware that doesn’t call next or send a response will prevent subsequent middleware from executing.
Best Practices
Keep middleware focused on a single responsibility. Use router.use for router-specific middleware. Always validate and sanitize user input. Use error-handling middleware to centralize error handling. Keep route handlers lean by delegating business logic to service layers.
Summary
Express provides a simple yet powerful framework for building Node.js web applications. Understanding the application object, middleware system, routing, and how requests flow through the application is essential for building maintainable and scalable backend applications.
By organizing code with routers, using middleware effectively, and following Express conventions, you can build applications that are both performant and easy to maintain.