Part 9 – Mastering Express Middleware

Middleware functions are the backbone of Express applications, handling everything from request processing to authentication. This guide will transform you into a middleware expert.

1. What is Middleware?

Key Characteristics

  • Functions that access req and res objects
  • Can execute any code
  • Can modify request/response objects
  • Can end the request-response cycle
  • Can call the next middleware (next())

Basic Structure

function myMiddleware(req, res, next) {
    // Do something with req/res
    next(); // Pass control to next middleware
}

// Usage
app.use(myMiddleware);

2. Types of Middleware

1. Application-Level Middleware

// Runs for every request
app.use((req, res, next) => {
    console.log(`Request received: ${req.method} ${req.path}`);
    next();
});

// Specific path
app.use('/admin', (req, res, next) => {
    console.log('Admin area access');
    next();
});

2. Router-Level Middleware

const router = express.Router();

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

3. Error-Handling Middleware

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

4. Built-In Middleware

app.use(express.json());       // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse form data
app.use(express.static('public')); // Serve static files

5. Third-Party Middleware

const helmet = require('helmet');
const cors = require('cors');

app.use(helmet()); // Security headers
app.use(cors());   // Enable CORS

3. Middleware Execution Flow

Understanding the order is crucial:

  1. Middleware executes in the order they’re defined
  2. Request flows through middleware until response is sent
  3. Error handlers only trigger when next(err) is called
  4. Route handlers are essentially terminal middleware

4. Practical Middleware Examples

Request Logging

app.use((req, res, next) => {
    const start = Date.now();

    res.on('finish', () => {
        const duration = Date.now() - start;
        console.log(`${req.method} ${req.url} - ${res.statusCode} (${duration}ms)`);
    });

    next();
});

Authentication

function authenticate(req, res, next) {
    const token = req.headers.authorization;

    if (!token) {
        return res.status(401).json({ error: 'Unauthorized' });
    }

    try {
        const user = verifyToken(token);
        req.user = user; // Attach user to request
        next();
    } catch (err) {
        next(err); // Pass to error handler
    }
}

// Protected route
app.get('/profile', authenticate, (req, res) => {
    res.json({ user: req.user });
});

5. Advanced Middleware Patterns

Conditional Middleware

function conditionalMiddleware(condition) {
    return (req, res, next) => {
        if (condition(req)) {
            // Apply middleware logic
            next();
        } else {
            next('route'); // Skip to next route
        }
    };
}

app.use(conditionalMiddleware(req => req.headers['x-special-header']));

Middleware Configuration

function configurableMiddleware(options = {}) {
    return (req, res, next) => {
        // Use options to customize behavior
        if (options.log) {
            console.log(`${req.method} ${req.url}`);
        }
        next();
    };
}

app.use(configurableMiddleware({ log: true }));

Async Middleware

const asyncMiddleware = fn => 
    (req, res, next) => {
        Promise.resolve(fn(req, res, next))
            .catch(next);
    };

app.use(asyncMiddleware(async (req, res, next) => {
    const data = await fetchData();
    req.data = data;
    next();
}));

6. Common Middleware Pitfalls

  • Forgetting to call next() – Causes hanging requests
  • Error handling order – Error middleware must come last
  • Modifying req/res too late – Headers already sent
  • Blocking operations – Defeats async benefits
  • Too many middleware – Impacts performance

7. Essential Third-Party Middleware

MiddlewarePurposeInstallation
helmetSecurity headersnpm install helmet
corsCross-Origin Resource Sharingnpm install cors
morganHTTP request loggingnpm install morgan
compressionResponse compressionnpm install compression
express-rate-limitRate limitingnpm install express-rate-limit

Next: Working with Databases →

Middleware Best Practices

  • Keep middleware focused on single responsibilities
  • Document custom middleware behavior
  • Place error handlers after other middleware
  • Use existing middleware solutions when possible
  • Test middleware in isolation

Leave a Comment

Your email address will not be published. Required fields are marked *