Middleware functions are an essential part of the Express.js framework. They 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. Middleware can execute any code, make changes to the request and response objects, end the request-response cycle, and call the next middleware function in the stack.
Key Concepts
- Definition: 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. - Execution Order: Middleware functions are executed sequentially, in the order they are defined.
- Types of Middleware:
- Application-level middleware: Bound to an instance of
express
. - Router-level middleware: Bound to an instance of
express.Router()
. - Error-handling middleware: Defined with four arguments instead of three.
- Built-in middleware: Provided by Express, such as
express.static
. - Third-party middleware: Created by the community, such as
body-parser
.
- Application-level middleware: Bound to an instance of
Practical Examples
Example 1: Application-Level Middleware
Application-level middleware is bound to an instance of express
using app.use()
or app.METHOD()
.
const express = require('express'); const app = express(); // Middleware function to log request details app.use((req, res, next) => { console.log(`${req.method} ${req.url}`); next(); // Pass control to the next middleware function }); app.get('/', (req, res) => { res.send('Hello, World!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Explanation:
- The middleware function logs the HTTP method and URL of each request.
- The
next()
function is called to pass control to the next middleware function.
Example 2: Router-Level Middleware
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(); // Middleware function to log request details router.use((req, res, next) => { console.log(`${req.method} ${req.url}`); next(); }); router.get('/', (req, res) => { res.send('Hello from the router!'); }); app.use('/router', router); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Explanation:
- The middleware function is applied to the router instance.
- The router is mounted on the
/router
path.
Example 3: Error-Handling Middleware
Error-handling middleware functions are defined with four arguments: (err, req, res, next)
.
const express = require('express'); const app = express(); // Middleware function to simulate an error app.use((req, res, next) => { const err = new Error('Something went wrong!'); next(err); // Pass the error to the error-handling middleware }); // Error-handling middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Internal Server Error'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Explanation:
- The first middleware function simulates an error by creating an
Error
object and passing it tonext()
. - The error-handling middleware logs the error stack and sends a 500 status code with a message.
Practical Exercises
Exercise 1: Logging Middleware
Task: Create a middleware function that logs the request method, URL, and timestamp for each request.
Solution:
const express = require('express'); const app = express(); // Logging middleware app.use((req, res, next) => { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] ${req.method} ${req.url}`); next(); }); app.get('/', (req, res) => { res.send('Hello, World!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Exercise 2: Authentication Middleware
Task: Create a middleware function that checks if a request has a valid API key in the query parameters. If the key is missing or invalid, respond with a 401 status code.
Solution:
const express = require('express'); const app = express(); // Authentication middleware app.use((req, res, next) => { const apiKey = req.query.apiKey; if (apiKey === '12345') { next(); // Valid API key, proceed to the next middleware } else { res.status(401).send('Unauthorized'); } }); app.get('/', (req, res) => { res.send('Hello, authenticated user!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Common Mistakes and Tips
- Forgetting to call
next()
: If you forget to callnext()
, the request will hang and the response will never be sent. - Order of middleware: The order in which middleware functions are defined matters. Ensure that middleware is defined before the routes that depend on it.
- Error-handling middleware: Always define error-handling middleware at the end of the middleware stack.
Conclusion
Middleware functions are a powerful feature of Express.js that allow you to modularize and organize your code effectively. By understanding and utilizing different types of middleware, you can handle various aspects of request processing, such as logging, authentication, and error handling, in a clean and maintainable way. In the next topic, we will delve into routing in Express.js, which will build upon the concepts learned here.
Node.js Course
Module 1: Introduction to Node.js
Module 2: Core Concepts
Module 3: File System and I/O
Module 4: HTTP and Web Servers
Module 5: NPM and Package Management
- Introduction to NPM
- Installing and Using Packages
- Creating and Publishing Packages
- Semantic Versioning
Module 6: Express.js Framework
- Introduction to Express.js
- Setting Up an Express Application
- Middleware
- Routing in Express
- Error Handling
Module 7: Databases and ORMs
- Introduction to Databases
- Using MongoDB with Mongoose
- Using SQL Databases with Sequelize
- CRUD Operations
Module 8: Authentication and Authorization
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with Mocha and Chai
- Integration Testing
- Debugging Node.js Applications
Module 10: Advanced Topics
Module 11: Deployment and DevOps
- Environment Variables
- Using PM2 for Process Management
- Deploying to Heroku
- Continuous Integration and Deployment