Middleware is a fairly common term when it comes to software computing. However, in my experience most young developers assume it to be a heavy concept. And so, it's rarely used to its full potential. I will try my best to simplify the idea and explain the what, why and hows of Middleware (in Node Js).
What’s with the name?
Middleware refers to a piece of code that sits between the client and server layers of an application, intercepting requests and responses as they flow through the system.
It's like a filter or a processing unit that intercepts incoming requests before they reach the application and performs certain tasks on them, such as authentication, logging, parsing, or validation. Once the middleware has completed its tasks, it passes the request along to the application or the next middleware in line.
Now, the most important part of Middleware lies in its simplicity. It receives the same Req
and Res
objects as the other Route Handlers receive. One of the most common Middleware you might have used is Body-Parser
. Middlewares alter these objects as needed and then the Route Handlers take over.
Creating a Middleware
To create a middleware in Node.js, you can use the use()
method provided by the Express framework. Here's an example of how to create a middleware function that logs incoming requests:
const express = require('express');
const app = express();
// Middleware function to log incoming requests
const myMiddleware= (req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
};
// Register middleware function with Express app
app.use(myMiddleware);
// Start server
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
In the above example, the `myMiddleware`
function is a middleware function that logs incoming requests to the console. It takes three arguments: req
, res
, and next
. req
represents the request object, res
represents the response object, and next
is a function that is called to pass control to the next middleware function in the chain.
To register the middleware function with the Express app, we use the use()
method and pass in the myMiddleware
function. This tells Express to use this middleware function for all incoming requests.
There are many different ways to create middleware in Node.js, which we will go over in a bit.
Structure of Middleware
In a typical Express setup, a middleware may look like this:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});
The order in which you apply middleware functions matters. Middleware functions are executed in the order they are added to the middleware stack.The next()
is very crucial for the next middleware to be run.
For example:
// Middleware function 1
const middleware1 = (req, res, next) => {
console.log('Middleware 1');
next();
};
// Middleware function 2
const middleware2 = (req, res, next) => {
console.log('Middleware 2');
next();
};
app.use(middleware1);
app.use(middleware2);
If we run this code the output we’ll get is:
Middleware 1
Middleware 2
However, if next() is not used:
// Middleware function 1
const middleware1 = (req, res, next) => {
console.log('Middleware 1');
};
// Middleware function 2
const middleware2 = (req, res, next) => {
console.log('Middleware 2');
next();
};
// Add middleware to the application
app.use(middleware1);
app.use(middleware2);
On running this code, we get the output:
Middleware 1
How to call Middleware functions?
In Node.js, there are a few different ways of calling middleware depending on the specific use case. Here are some examples:
All routes using
app.use()
: This method allows you to define middleware that will be called for every route in your application. This is useful for tasks like logging, authentication, and error handling.const express = require('express'); const app = express(); // Middleware function to log all requests const logger = (req, res, next) => { console.log(`Received ${req.method} request for ${req.url}`); next(); }; // Call the middleware for all routes app.use(logger); // Define routes app.get('/', (req, res) => { res.send('Hello, world!'); }); // Start the server app.listen(3000, () => { console.log('Server listening on port 3000'); });
Specific routes using
app.use()
: This method allows you to define middleware that will be called for specific routes in your application. This is useful when you need to perform certain tasks only for certain routes.const express = require('express'); const app = express(); // Middleware function to check for admin privileges const checkAdmin= (req, res, next) => { if (req.user.role !== 'admin') { res.status(403).send('Access denied'); } else { next(); } }; // Call the middleware for a specific route app.use('/admin', checkAdmin); app.get('/admin', (req, res) => { res.send('Welcome to the admin panel!'); }); // Start the server app.listen(3000, () => { console.log('Server listening on port 3000'); });
Inline with route definition: This method allows you to define middleware that will be called for a specific route inline with the route definition. This is useful when you need to perform a task only for a specific route and don't want to define a separate middleware function.
const express = require('express'); const app = express(); app.get('/users', (req, res, next) => { // Inline middleware to check for query parameter if (req.query.search) { next(); } else { res.status(400).send('Missing search query parameter'); } }, (req, res) => { // Handler function for the route const searchQuery = req.query.search; res.send(`Search results for "${searchQuery}"`); }); // Start the server app.listen(3000, () => { console.log('Server listening on port 3000'); });
I hope you learnt some important concepts from this article and now have a better understanding of what Middlewares are.