MIDDLEWARE IN EXPRESS
Middleware in Express.js refers to functions that have access to the request and
response objects, as well as the next middleware function in the application’s
request-response cycle.
These functions can perform various operations, including executing code,
modifying the request and response objects, terminating the request-response
cycle, handling cookies, logging requests, and passing control to subsequent
middleware functions.
Types of middlewares
1. Application-level middleware: Binds to an instance of the app object.
2. Router-level middleware: Binds to an instance of express.Router().
3. Error-handling middleware: Special middleware that handles errors.
4. Built-in middleware: Provided by Express, such
as express.json() and express.static().
Implementing middleware
Implementing middleware in Express.js involves using app.use() for application-
level middleware and router.use() for router-level middleware. Middleware
functions can be chained and executed sequentially, allowing for complex request
processing and response handling. Error-handling middleware is defined with four
arguments and used after other middleware and route definitions. Express also
provides built-in middleware for common tasks, and you can also utilize third-
party middleware to extend functionality.
Application-level middleware
Application-level middleware is attached to an instance of the app object
through app.use() and app.METHOD(), where the METHOD is an HTTP method.
Example
const express = require('express');
const app = express();
// Application-level middleware
app.use((req, res, next) => {
console.log('Application-level middleware.');
console.log(`Request method: ${req.method}, URL: ${req.url}, Time: $
{Date.now()}`);
next(); // Proceed to the next middleware
});
// Route handler for the root URL
app.get('/', (req, res) => {
res.send('Hello, World!');
});
// Start the server
app.listen(3000, () => {
console.log('The server is running on port 3000.');
});
Router-level middleware
Router-level middleware works like application-level middleware, except it is
bound to an instance of express.Router(). Router-level middleware can be scoped
to specific URL paths within a router.
Example
const express = require('express');
const app = express();
const router = express.Router();
// Middleware function for router
router.use((req, res, next) => {
console.log('Request URL:', req.originalUrl);
next(); // Proceed to the next middleware
});
// Route handler
router.get('/', (req, res) => {
res.send('Router Middleware');
});
app.use('/router', router); // Apply router middleware
app.listen(3000, () => {
console.log('The server is running on port 3000.');
});
Error-handling middleware
Error-handling middleware functions have four arguments: (err, req, res, next).
They are defined after other app.use() and route calls.
Example:
const express = require('express');
const app = express();
// Middleware to trigger an error
app.get('/', (req, res) => {
throw new Error('BROKEN'); // Express will catch this on its own.
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
app.listen(3000, () => {
console.log('The server is running on port 3000.');
});
Built-in middleware
Express provides built-in middleware to handle common tasks. For
instance, express.json() and express.urlencoded() parse JSON and URL-encoded
data.
Example
const express = require('express');
const app = express();
// Built-in middleware to parse JSON
app.use(express.json());
app.post('/data', (req, res) => {
res.send(`Received data: ${JSON.stringify(req.body)}`);
});
app.listen(3000, () => {
console.log('The server is running on port 3000.');
});
A demonstration of how middleware works
Server.js
const express = require('express'); // Importing express
const app = express(); // Creating an express app
const homeRoutes =require ('./routes/homeRoutes')
const customMiddleware = require('./middleware/customMiddleware');
// Application-level middleware (runs for all routes)
app.use(express.json()); // Built-in middleware to parse JSON bodies
app.use(express.urlencoded({ extended: true })); // For form data
app.use(customMiddleware.logger); // Our custom logger middleware
// Route-specific middleware example
app.use('/protected', customMiddleware.authCheck);
// Use routes
app.use('/', homeRoutes);
// Error handling middleware (should be last)
app.use(customMiddleware.errorHandler);
// Set up the server to listen on port 3000
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
HomeRoute.js
const express = require('express');
const router = express.Router();
const customMiddleware = require('../middleware/customMiddleware');
// Middleware that runs only for this specific route
router.get('/special', customMiddleware.authCheck, (req, res) => {
res.send('<h1>This is a special route with auth check!</h1>');
});
// Homepage route
router.get('/', (req, res) => {
res.send('<h1>Hello, Express.js Server!</h1>');
});
router.get('/protected', (req, res) => {
res.send('<h1>Protected Content</h1>');
});
// Export the router
module.exports = router;
customMiddleware.js
// Custom middleware examples
const customMiddleware = {
// Logger middleware to log request details
logger: (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
},
// Authentication check (simple example)
authCheck: (req, res, next) => {
// In a real app, you'd check for a valid token or session
console.log("Authentication check running...");
next();
},
// Error handling middleware
errorHandler: (err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
}
};
module.exports = customMiddleware;
You can test these by:
1. Visiting http://localhost:3000/ (normal route)
2. Visiting http://localhost:3000/protected (route with auth middleware)
3. Visiting http://localhost:3000/special (route with specific middleware)
NOTE: Middleware in Express.js are functions that have access to the request
(req), response (res), and the next function in the application's request-response
cycle. They can:
Modify req and res objects (e.g., parse JSON, add headers).
Execute code (e.g., logging, authentication checks).
End the request-response cycle (e.g., send an error response).
Call the next middleware using next().
How Middleware Works Step-by-Step
Example 1: Visiting GET /
1. Request → GET /
2. express.json() → Does nothing (no JSON body in GET)
3. express.urlencoded() → Does nothing (no form data)
4. logger → Logs [timestamp] GET /
5. Route handler → Sends <h1>Hello, Express.js Server!</h1>
Example 2: Visiting GET /special
1. Request → GET /special
2. express.json() → Does nothing
3. express.urlencoded() → Does nothing
4. logger → Logs [timestamp] GET /special
5. authCheck → Logs "Authentication check running..."
6. Route handler → Sends <h1>This is a special route with auth check!</h1>
Example 3: Sending POST / with JSON
1. Request → POST / with {"name": "John"}
2. express.json() → Parses JSON into req.body = { name: "John" }
3. express.urlencoded() → Does nothing
4. logger → Logs [timestamp] POST /
5. Route handler → Can now access req.body.name