HVRDHVRD
ExpressJS

Middlewares

Comprehensive guide to understanding and using middlewares in ExpressJS for request handling, processing, and routing control.

Middlewares in ExpressJS

In ExpressJS, a middleware is a function that has access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle.

Middlewares can:

  • Execute any code.
  • Modify req and res objects.
  • End the request-response cycle.
  • Call the next middleware in the stack.

Middleware Signature

A middleware function looks like this:

function middleware(req, res, next) {
  // Perform operations
  next(); // Pass control to the next middleware
}
  • req: The incoming request object.
  • res: The outgoing response object.
  • next: Function to pass control to the next middleware.

Types of Middleware

1. Application-Level Middleware

Defined using app.use() or HTTP methods like app.get(), app.post().

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

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

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

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

2. Router-Level Middleware

Attached to an express.Router() instance.

const express = require('express');
const router = express.Router();

// Router-specific middleware
router.use((req, res, next) => {
  console.log('Router middleware');
  next();
});

router.get('/items', (req, res) => {
  res.send('List of items');
});

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

3. Error-Handling Middleware

Error-handling middleware has four arguments: (err, req, res, next).

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

Always defined after other middlewares and routes.


4. Built-In Middleware

Express provides some built-in middleware like:

  • express.static(): Serves static files.
  • express.json(): Parses incoming JSON payload.
  • express.urlencoded(): Parses URL-encoded payload.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));

5. Third-Party Middleware

Popular middlewares from npm:

  • morgan: HTTP request logger.
  • cors: Enables Cross-Origin Resource Sharing.
  • helmet: Adds security-related HTTP headers.

with Morgan

const morgan = require('morgan');

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

Middleware in Route Syntax (app.get, app.post, etc.)

Middlewares can be directly passed in the route definition.

function checkAuth(req, res, next) {
  const token = req.headers['authorization'];
  if (token === 'valid-token') {
    next();
  } else {
    res.status(401).json({ message: 'Unauthorized' });
  }
}

app.get('/dashboard', checkAuth, (req, res) => {
  res.send('Welcome to your dashboard');
});
  • checkAuth runs first.
  • If checkAuth calls next(), the final handler executes.
  • If not, the response ends early.

You can chain multiple middlewares:

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

function checkAdmin(req, res, next) {
  const isAdmin = req.headers['admin'] === 'true';
  if (isAdmin) {
    next();
  } else {
    res.status(403).send('Forbidden');
  }
}

app.get('/admin', logRequest, checkAdmin, (req, res) => {
  res.send('Welcome, Admin');
});

Controlling the Flow

A middleware can decide whether to:

  1. Pass control to the next middleware:

    function logger(req, res, next) {
      console.log('Logging request');
      next();
    }
  2. End the request-response cycle:

    function blockRequest(req, res, next) {
      res.status(403).send('Access Denied');
      // No next() call, request ends here
    }

Middleware Ordering Matters

Middlewares are executed in the order they are added.

app.use(middlewareOne);
app.use(middlewareTwo);
app.get('/path', (req, res) => res.send('Done'));

middlewareOne runs first, then middlewareTwo, then the route handler.


Practical Example

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

app.use(express.json());

// Authentication middleware
function authMiddleware(req, res, next) {
  const token = req.headers['authorization'];
  if (token === 'valid-token') {
    next();
  } else {
    res.status(401).json({ message: 'Unauthorized' });
  }
}

// Apply authMiddleware to protected routes
app.use('/protected', authMiddleware);

app.get('/protected/data', (req, res) => {
  res.json({ secret: 'This is protected data' });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Best Practices

  • Keep middlewares small and single-purpose.
  • Place error-handling middleware last.
  • Use third-party middlewares for common tasks.
  • Avoid blocking the event loop (no heavy synchronous tasks).
  • Use next(err) to delegate error handling.
  • Don’t forget to call next() unless the response ends.