What Is Middleware in Express?
Middleware functions are functions that execute during the request-response cycle. They have access to the request object (req
), the response object (res
), and the next
callback, which passes control to the next middleware function in the chain. Middleware can perform a variety of task such as logging, parsing request bodies, handling authentication, serving static files, and error handling.
1️⃣ Built-in Middleware
Express comes with several built-in middleware functions that help you handle common tasks without needing extra packages. Some examples include:
express.json()
Parse incoming requests with JSON payloads.
app.use(express.json());
express.urlencoded()
Parse URL-encoded payloads, typically used for HTML form submissions.
app.use(express.urlencoded({ extended: true}));
express.static()
Serves static files (e.g., images, CSS, JavaScript) from a designated folder.
app.use(express.static('public'));
2️⃣ Third-Party Middleware
Third-party middleware extends Express's functionality by providing additional features that aren't included in the core framework. They are installed via npm and then integrated into your app using app.use()
(or at the router level). Some popular examples include:
cookie-parser
Parses cookies attached to the client request.
npm install cookie-parser
const cookieParser = require('cookie-parser'); app.use(cookieParser());
morgan
Provides HTTP request logging.
npm install morgan
const morgan = require('morgan'); app.use(morgan('dev'));
helmet
Enhances security by setting various HTTP headers.
npm install helmet
const helmet = require('helmet'); app.use(helmet());
cors
Enables Cross-Origin Resource Sharing, allowing your app to handle requests from different domains.
npm install cors
const cors = require('cors'); app.use(cors());
3️⃣ Custom Middleware
Custom middleware functions are written by you to perform application-specific tasks. They follow the standard middleware signature.
function customMiddleware(req, res, next) {
// Your custom logic here.
next(); // Call next() to pass control.
}
Example: Request Logger
const express = require('express');
const app = express();
// Custom middleware to log the time and requested URL
const requestLogger = (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
};
// Apply the custom middleware globally
app.use(requestLogger);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
Example: Authentication Checker (Route-Specific Middleware)
// Custom middleware to check for a valid token in headers
const checkAuth = (req, res, next) => {
if (req.headers.authorization === 'my-secret-token') {
next();
} else {
res.status(401).send('Unauthorized');
}
};
// Use the middleware only for the /dashboard route
app.get('/dashboard', checkAuth, (req, res) => {
res.send('Welcome to the Dashboard!');
});
Middleware Execution Flow and Best Practices
- Chaining and Order:
- Middleware functions are executed in the order they are added with
app.use()
(or at the router level). This order is critical – built-in middleware should typically be loaded before custom routes, and error-handling middleware should come lat.
- Middleware functions are executed in the order they are added with
- Single Responsibility:
- Keep middleware functions focused on a single task to maintain readability and ease of maintenance.
- Error Handling:
Write dedicated error-handling middleware (with four parameters:
err, req, res, next
) to gracefully manage errors in the application.app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something went wrong!'); });