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

  1. 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.
  2. Execution Order: Middleware functions are executed sequentially, in the order they are defined.
  3. 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.

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 to next().
  • 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 call next(), 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

Module 6: Express.js Framework

Module 7: Databases and ORMs

Module 8: Authentication and Authorization

Module 9: Testing and Debugging

Module 10: Advanced Topics

Module 11: Deployment and DevOps

Module 12: Real-World Projects

© Copyright 2024. All rights reserved