In this chapter, we'll delve into the concept of middleware in Express.js, exploring its basics and advanced usage with practical examples. Middleware is a fundamental part of Express.js, playing a crucial role in building robust web applications. Understanding middleware will help you handle various tasks such as request processing, authentication, logging, error handling, and more.
Middleware functions are functions that have access to the request object (req
), the response object (res
), and the next middleware function in the application’s request-response cycle. These functions can perform a variety of tasks:
Application-level middleware is bound to an instance of the app
object using app.use()
or app.METHOD()
, where METHOD
is an HTTP method such as GET, POST, PUT, etc.
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
next()
.Output: When you navigate to http://localhost:3000/
, the server logs the current time and responds with “Hello, World!”.
Router-level middleware works in the same way as application-level middleware, except it is bound to an instance of express.Router()
.
const express = require('express');
const app = express();
const router = express.Router();
router.use((req, res, next) => {
console.log('Request URL:', req.originalUrl);
next();
});
router.get('/', (req, res) => {
res.send('Router Home');
});
app.use('/router', router);
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
/router
path and responds with “Router Home”.Output: When you navigate to http://localhost:3000/router
, the server logs the requested URL and responds with “Router Home”.
Error-handling middleware functions have four arguments: (err, req, res, next)
. They are used to catch and handle errors.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
throw new Error('Something went wrong!');
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
will result in a 500 status response with the message “Something broke!”.
Express has several built-in middleware functions, such as express.static
, express.json
, and express.urlencoded
.
const express = require('express');
const app = express();
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
express.static
middleware serves static files from the public
directory.You can also use third-party middleware, such as morgan
for logging, helmet
for security, and cors
for enabling Cross-Origin Resource Sharing.
Express has several built-in middleware functions, such as express.static
, express.json
, and express.urlencoded
.
Middleware functions are executed in the order they are added. Each middleware function receives three arguments: req
, res
, and next
. The next
function is a callback that passes control to the next middleware function.
To create middleware, you simply define a function with req
, res
, and next
as parameters.
const express = require('express');
const app = express();
const myMiddleware = (req, res, next) => {
console.log('Request received');
next();
};
app.use(myMiddleware);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
logs “Request received” and responds with “Hello, World!”.
The order in which middleware functions are defined is important because they are executed sequentially.
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('First middleware');
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
logs “First middleware”, then “Second middleware”, and responds with “Hello, World!”.
The express.static
middleware serves static files, such as HTML, CSS, and JavaScript files, from a directory.
const express = require('express');
const app = express();
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: If you have an index.html
file in the public
directory, navigating to http://localhost:3000/
will serve that file.
Express provides middleware to parse incoming request bodies.
Body-parser is now part of the core express
package, allowing you to parse incoming request bodies.
const express = require('express');
const app = express();
app.use(express.json());
app.post('/data', (req, res) => {
res.send(req.body);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Using Postman, send a POST request to http://localhost:3000/data
with a JSON body { "name": "John" }
. The response will be { "name": "John" }
.
This middleware is used to parse URL-encoded bodies, which are typically sent via HTML forms.
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post('/form', (req, res) => {
res.send(req.body);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Using Postman or an HTML form, send a POST request to http://localhost:3000/form
with form data name=John
. The response will be { "name": "John" }
.
Custom middleware allows you to add any functionality you need.
const express = require('express');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
};
app.use(logger);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Every time you navigate to any endpoint, the method and URL will be logged to the console.
const express = require('express');
const app = express();
const authenticate = (req, res, next) => {
const token = req.headers['authorization'];
if (token === 'mysecrettoken') {
next();
} else {
res.status(401).send('Unauthorized');
}
};
app.use(authenticate);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigate to http://localhost:3000/
with an authorization header set to mysecrettoken
to receive “Hello, World!”. Otherwise, you receive a 401 status with “Unauthorized”.
const express = require('express');
const app = express();
const requestTime = (req, res, next) => {
req.requestTime = Date.now();
next();
};
app.use(requestTime);
app.get('/', (req, res) => {
res.send(`Current Time: ${req.requestTime}`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigate to http://localhost:3000/
to see the current request time.
Error-handling middleware catches errors and prevents them from crashing the server.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
throw new Error('Something went wrong!');
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
logs the error stack and responds with “Something broke!”.
Morgan is a popular logging middleware.
const express = require('express');
const morgan = require('morgan');
const app = express();
app.use(morgan('tiny'));
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Each request is logged in a ‘tiny’ format.
Helmet helps secure your Express apps by setting various HTTP headers.
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.get('/', (req, res) => {
res.send('Hello, Secure World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: HTTP headers for security are automatically set.
Cors middleware allows you to enable CORS with various options.
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/', (req, res) => {
res.send('CORS enabled!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: CORS is enabled for all routes.
Middleware functions can be chained by calling next()
within each function.
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('First middleware');
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
logs “First middleware”, then “Second middleware”, and responds with “Hello, World!”.
Middleware composition allows you to group multiple middleware functions into a single function.
const express = require('express');
const app = express();
const middleware1 = (req, res, next) => {
console.log('Middleware 1');
next();
};
const middleware2 = (req, res, next) => {
console.log('Middleware 2');
next();
};
const composedMiddleware = [middleware1, middleware2];
app.use(composedMiddleware);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Output: Navigating to http://localhost:3000/
logs “Middleware 1”, then “Middleware 2”, and responds with “Hello, World!”.
Let’s create a practical example of an Express.js application that demonstrates various types of middleware. We’ll build a simple user management system with the following features:
First, create a new directory for your project and initialize it with npm:
mkdir express-middleware-example
cd express-middleware-example
npm init -y
Then, install Express and other necessary middleware:
npm install express morgan body-parser helmet cors
Create a new file named server.js
and start by setting up the basic Express server:
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const cors = require('cors');
const app = express();
const PORT = 3000;
// Middleware setup
app.use(helmet()); // Security headers
app.use(cors()); // Enable CORS
app.use(morgan('tiny')); // Request logging
app.use(bodyParser.json()); // JSON body parsing
app.use(express.static('public')); // Serve static files
// Custom middleware for logging request details
app.use((req, res, next) => {
console.log(`Request Method: ${req.method}, Request URL: ${req.url}`);
next();
});
// Basic authentication middleware
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (authHeader && authHeader === 'Bearer mysecrettoken') {
next();
} else {
res.status(401).send('Unauthorized');
}
};
// Routes
app.get('/', (req, res) => {
res.send('Welcome to the User Management System!');
});
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === 'admin' && password === 'password') {
res.send({ token: 'mysecrettoken' });
} else {
res.status(401).send('Invalid credentials');
}
});
app.get('/users', authenticate, (req, res) => {
res.send(['User1', 'User2', 'User3']);
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Create a directory named public
in the root of your project and add an index.html
file inside it:
User Management System
Welcome to the User Management System
Navigate to /users
to see the list of users.
Run the server by executing the following command:
node server.js
Static File Serving: Navigate to http://localhost:3000/
to see the index.html
file served from the public
directory.
Request Logging: Check the terminal to see request logs generated by Morgan and the custom logging middleware.
JSON Body Parsing: Use Postman or any API testing tool to send a POST request to http://localhost:3000/login
with a JSON body:
{
"username": "admin",
"password": "password"
}
You should receive a response with a token if the credentials are correct.
Basic Authentication: Use the token received from the login endpoint to access the /users
endpoint. Send a GET request to http://localhost:3000/users
with an Authorization
header set to Bearer mysecrettoken
. You should receive a list of users.
Error Handling: If there is an error in the application, it will be caught by the error-handling middleware, and a 500 status response will be sent.
In this chapter, we explored middleware in Express.js from basics to advanced concepts. We discussed different types of middleware, how they work, and how to create and use them effectively. Middleware is a powerful feature in Express.js, enabling you to add a wide range of functionalities to your applications. By mastering middleware, you can build more robust, secure, and maintainable Express.js applications.Happy coding !❤️