Writing Middleware in Express.js: A Complete Guide for Developers

Middleware is one of the core architectural concepts in Express.js. . Middleware functions have access to the request object (req), the response object (res), and the next() function, which controls the flow of the request–response cycle. Middleware can modify requests, send responses, validate data, log activity, handle errors, or pass control to the next function in the stack. This article explains everything you need to know about writing middleware—from simple examples to async validation, error handling, and configurable middleware.

Express Middlewarenext()Request-Response CycleAsync MiddlewareError HandlingConfigurable Middleware

~3 min read • Updated Dec 26, 2025

1. What Is Middleware in Express?


A middleware function in Express has access to:

  • req – the request object
  • res – the response object
  • next – a function that passes control to the next middleware

Middleware can:

  • Execute any code
  • Modify req or res
  • End the request–response cycle
  • Call next() to continue the chain

If a middleware does not end the response, it must call next() or the request will hang.

2. A Simple Example


const express = require('express')
const app = express()

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

app.listen(3000)

We will now add three middleware functions to this app.

3. Middleware Example: myLogger


This middleware logs a simple message for every request.

const myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

Load it using app.use():

app.use(myLogger)

Important notes:

  • Middleware runs in the order it is loaded.
  • If placed after a route handler, it may never run.
  • next() must be called to continue the chain.

4. Middleware Example: requestTime


This middleware adds a timestamp to the request object.

const requestTime = function (req, res, next) {
  req.requestTime = Date.now()
  next()
}

Usage:

app.use(requestTime)

app.get('/', (req, res) => {
  res.send(`Requested at: ${req.requestTime}`)
})

5. Async Middleware Example: validateCookies


This middleware validates cookies using an external async service.

async function validateCookies (req, res, next) {
  await cookieValidator(req.cookies)
  next()
}

In Express 5, async middleware automatically triggers next(error) if it throws or rejects.

Usage with cookie-parser:

app.use(cookieParser())
app.use(validateCookies)

app.use((err, req, res, next) => {
  res.status(400).send(err.message)
})

If you pass anything to next() (except 'route' or 'router'), Express treats it as an error.

6. Configurable Middleware


To create middleware that accepts options, export a function that returns the middleware implementation.

my-middleware.js

module.exports = function (options) {
  return function (req, res, next) {
    // Use options here
    next()
  }
}

Usage:

const mw = require('./my-middleware')
app.use(mw({ option1: '1', option2: '2' }))

Examples of configurable middleware: cookie-session, compression.

7. Key Takeaways


  • Middleware executes in the order it is defined.
  • If next() is not called, the request will hang.
  • Async middleware automatically forwards errors.
  • You can modify req and res freely.
  • You can end the response or pass control onward.
  • Middleware can be modular and configurable.

Conclusion


Middleware is the heart of Express.js. It enables logging, validation, error handling, request transformation, and modular architecture. With a solid understanding of middleware, you can build flexible, maintainable, and powerful Express applications.

Written & researched by Dr. Shahin Siami