Camkode
Camkode

How to Use Middleware Effectively in Express

Posted by Kosal

Express.js, the minimalist web framework for Node.js, owes much of its flexibility and power to middleware. Middleware functions are the backbone of Express apps—they can process requests, modify responses, and control application flow.

If you're building scalable and maintainable applications, understanding and using middleware effectively is essential. In this article, we'll explore what middleware is, how it works, and best practices for using it in your Express apps.

What Is Middleware?

In Express, middleware is a function that has access to the request (req), response (res), and the next function in the application’s request-response cycle. Middleware functions can:

  • Execute code
  • Modify the request or response
  • End the request-response cycle
  • Call the next() middleware in the stack

Basic Syntax

function middlewareExample(req, res, next) {
  // Do something with req or res
  next(); // Call next middleware
}

You use it in an Express app like this:

app.use(middlewareExample);

Types of Middleware in Express

  1. Application-Level Middleware

    Bound to an instance of express() using app.use() or app.METHOD().

  2. Router-Level Middleware

    Similar to application-level but bound to an instance of express.Router().

  3. Error-Handling Middleware

    Has four arguments: (err, req, res, next). Used for catching and handling errors.

  4. Built-in Middleware

    Express provides built-in middleware such as express.json() and express.static().

  5. Third-Party Middleware

    Middleware provided by the community, like morgan, cors, body-parser, etc.

How Middleware Works (Request Flow)

When a request is made to the Express server, it flows through the middleware stack in the order they are defined:

app.use(middleware1);
app.use(middleware2);
app.get('/', (req, res) => {
  res.send('Hello World!');
});

If middleware1 calls next(), then middleware2 is executed. Otherwise, the response ends there.

Practical Examples

Logging Middleware

app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

JSON Parsing Middleware

app.use(express.json());

Authentication Middleware

function authenticate(req, res, next) {
  if (req.headers.authorization === 'Bearer valid-token') {
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
}

app.use('/api', authenticate);

Error-Handling Middleware

To handle errors gracefully:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something went wrong!');
});

Ensure it's defined after all other app.use() and route calls.

Best Practices for Using Middleware

  1. Keep Middleware Single-Purpose

    Each middleware should do one thing—log, authenticate, validate, etc.

  2. Use Router-Level Middleware

    Attach middleware to routers to keep concerns isolated.

    const userRouter = express.Router();
    userRouter.use(authenticate);
    userRouter.get('/profile', (req, res) => { ... });
    app.use('/users', userRouter);
    
  3. Avoid Side Effects

    Middleware should not unintentionally modify shared objects unless necessary.

  4. Use Third-Party Middleware When Appropriate

    Libraries like helmet (security), cors, and morgan save time and improve reliability.

  5. Order Matters

    Define middleware in the correct order—logging, parsing, authentication, routing, then error handling.

Conclusion

Middleware is one of the most powerful and flexible features in Express.js. Whether you're logging requests, securing routes, or catching errors, middleware allows you to build modular and maintainable applications.

By following best practices and structuring your middleware wisely, you can keep your Express apps clean, efficient, and scalable.