Paramclasses Academy - Blog

Tutorials Details

←
Post 19 of 40
→

Middleware in Express.js: Types, Uses and Custom Middleware

Middleware in Express.js: Types, Uses and Custom Middleware

October 29, 2026

✦ Middleware in Express.js: Types, Uses and Custom Middleware

❖ Introduction to Middleware in Express.js

Express.js is a popular Node.js framework for building web applications and REST APIs. One of its most useful features is middleware.

Middleware functions run during the request-response cycle. They can execute code, read or modify request and response objects, end a request, or pass control to the next middleware function.

For example, when a user logs in to a website, middleware can check authentication before allowing access to a protected page. Middleware can also log requests, parse JSON data, validate input, and handle errors.

In this tutorial, we will learn about Express.js middleware, its types, practical examples, and how to create custom middleware for real-world applications.

✦ What Is Middleware in Express.js?

Middleware is a function that receives the request object (req), the response object (res), and usually a next function.

A basic middleware function looks like this:

function myMiddleware(req, res, next) {
    console.log("Middleware executed");

    next();
}

Here:

  • req contains information about the incoming request.

  • res is used to send a response to the client.

  • next() passes control to the next middleware or route handler.

Middleware functions execute in the order in which they are registered, subject to the route and error-handling flow.

✦ Step 1: Set Up an Express.js Project

First, create a new Node.js project and install Express.js.

mkdir express-middleware
cd express-middleware
npm init -y
npm install express

Create a file named app.js:

const express = require("express");

const app = express();
const PORT = 3000;

app.get("/", (req, res) => {
    res.send("Welcome to the Express.js Middleware Tutorial!");
});

app.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`);
});

Start the application:

node app.js

Visit http://localhost:3000 in your browser to check that the server is running.

✦ Step 2: Create Your First Custom Middleware

Let's create middleware that logs every incoming request.

const express = require("express");

const app = express();

function requestLogger(req, res, next) {
    console.log(`${req.method} ${req.originalUrl}`);

    next();
}

app.use(requestLogger);

app.get("/", (req, res) => {
    res.send("Home Page");
});

app.get("/about", (req, res) => {
    res.send("About Page");
});

app.listen(3000, () => {
    console.log("Server running on port 3000");
});

When you visit / or /about, the terminal displays the request method and URL.

For example:

GET /
GET /about

The statement app.use(requestLogger) registers middleware for incoming requests that reach it.

Important: If middleware neither sends a response nor calls next(), the request may remain pending. Always ensure that each execution path either completes the response or passes control appropriately.

✦ Step 3: Understand the Main Types of Middleware

Express.js supports several types of middleware.

❖ 1. Application-Level Middleware

Application-level middleware is registered using app.use() or methods such as app.get() and app.post().

app.use((req, res, next) => {
    console.log("Application-level middleware");

    next();
});

Use this type when functionality needs to run across many routes, such as request logging or general request processing.

❖ 2. Built-In Middleware

Express includes middleware for common tasks.

JSON parsing:

app.use(express.json());

This allows the application to read JSON request bodies through req.body.

URL-encoded form parsing:

app.use(express.urlencoded({ extended: true }));

This parses URL-encoded form submissions, such as typical HTML form data.

Register these middleware functions before the routes that need their parsed request bodies.

❖ 3. Router-Level Middleware

Router-level middleware works with an Express Router.

const express = require("express");

const router = express.Router();

router.use((req, res, next) => {
    console.log("Student router middleware");
    next();
});

router.get("/", (req, res) => {
    res.send("Student List");
});

app.use("/students", router);

This example assumes app has already been created with express(). The middleware applies to requests entering this router, such as /students.

Router-level middleware is helpful for organizing large applications into separate modules.

❖ 4. Third-Party Middleware

Third-party middleware is installed from npm to add functionality.

For example, Morgan is commonly used for HTTP request logging.

Install it:

npm install morgan

Use it in your application:

const morgan = require("morgan");

app.use(morgan("dev"));

This example assumes app is your Express application. Morgan prints useful request information in the terminal.

Other third-party middleware packages can help with security headers, cross-origin requests, and request rate limiting. Select packages according to your application's requirements.

❖ 5. Error-Handling Middleware

Error-handling middleware has four parameters: err, req, res, and next.

app.use((err, req, res, next) => {
    console.error(err.stack);

    res.status(500).json({
        message: "Something went wrong"
    });
});

Register error-handling middleware after your routes and other relevant middleware.

In a real application, avoid returning stack traces or confidential error details to users in production.

✦ Step 4: Create Route-Specific Middleware

Sometimes middleware should run only for particular routes.

For example, imagine an admin dashboard that requires an authorization check.

function checkAdmin(req, res, next) {
    const isAdmin = false; // Demonstration only

    if (!isAdmin) {
        return res.status(403).json({
            message: "Access denied"
        });
    }

    next();
}

app.get("/admin", checkAdmin, (req, res) => {
    res.send("Welcome to the Admin Dashboard");
});

Because isAdmin is set to false, this demonstration returns a 403 Forbidden response.

In a real application, the middleware must verify a trusted login session or token and check the user's permissions. Do not rely on a hardcoded value or a user-supplied role to grant access.

Route-specific middleware is useful for protecting admin pages, restricting certain API operations, and validating incoming data.

✦ Step 5: Access Request Data Inside Middleware

Middleware can read request details and add useful information to the request object for later handlers.

function addRequestTime(req, res, next) {
    req.requestTime = new Date().toISOString();

    next();
}

app.use(addRequestTime);

app.get("/time", (req, res) => {
    res.json({
        message: "Request received",
        requestTime: req.requestTime
    });
});

When a client requests /time, the route returns the timestamp added by the middleware.

This pattern can be useful for request IDs, timing information, and application-specific context. Avoid storing sensitive information unnecessarily on the request object.

✦ Step 6: Understand Middleware Execution Order

Middleware order matters in Express.js.

Consider this example:

app.use((req, res, next) => {
    console.log("First middleware");
    next();
});

app.use((req, res, next) => {
    console.log("Second middleware");
    next();
});

app.get("/", (req, res) => {
    console.log("Route handler");
    res.send("Hello from Express.js");
});

When you visit /, the terminal displays:

First middleware
Second middleware
Route handler

This demonstrates the normal execution flow.

If a middleware sends a response without calling next(), subsequent handlers generally will not execute for that request. If middleware calls next(err), Express transfers control to error-handling middleware.

Always register middleware in the correct order so that request parsing, authentication, and other required checks happen before the relevant routes.

✦ Common Middleware Mistakes to Avoid

  1. Forgetting next(): A middleware that does not send a response or pass control can leave a request hanging.

  2. Incorrect registration order: JSON parsing middleware should run before routes that read req.body.

  3. Sending multiple responses: Avoid sending a second response after one has already been sent.

  4. Ignoring errors: Forward errors to appropriate error-handling middleware.

  5. Trusting user input: Validate request data before using it in business logic or database operations.

  6. Weak authentication checks: Verify identity and permissions using a secure authentication mechanism.

  7. Exposing internal errors: Keep detailed stack traces in server logs rather than public production responses.

✦ Frequently Asked Questions (FAQs)

1. What is middleware in Express.js?

Middleware is a function that runs during the request-response cycle and can process requests, send responses, or pass control to another handler.

2. What does next() do?

The next() function passes control to the next applicable middleware or route handler.

3. What is the difference between application-level and router-level middleware?

Application-level middleware is registered on the main Express application, while router-level middleware is registered on an Express Router.

4. What is error-handling middleware?

It is middleware with four parameters—err, req, res, and next—used to process errors in an Express application.

5. Can we use multiple middleware functions in one application?

Yes. Express applications commonly use multiple middleware functions for logging, parsing request bodies, authentication, validation, and error handling.

✦ Conclusion

Middleware is a fundamental part of Express.js development. It allows developers to organize request processing into reusable functions and implement features such as logging, authentication, validation, and error handling.

Start by creating a simple custom middleware, understand how next() works, and then experiment with built-in and third-party middleware. As your Node.js skills grow, middleware will help you create more maintainable web applications and secure REST APIs.

Learn practical backend development with Param Classes.

←
Post 19 of 40
→