How to Explain Express.js Middleware in Your Interview

·9 min read
nodejsexpressinterview-questionsmiddlewarebackend

Express.js powers over 6.3 million websites and handles billions of requests daily—all orchestrated through a deceptively simple concept: middleware. One forgotten next() call, and requests hang forever. One misplaced error handler, and your entire error strategy breaks. Here's everything you need to ace this question.

The 30-Second Answer

When the interviewer asks "What is middleware in Express?", here's your concise answer:

"Middleware functions are functions that execute during the request-response cycle. They have access to the request object, response object, and a next function. Middleware can execute code, modify the request and response, end the cycle by sending a response, or call next() to pass control to the next middleware in the stack."

Short, accurate, and shows you understand the core concept.

The 2-Minute Answer (If They Want More)

If they ask for more detail:

"Express middleware works like a pipeline. Each request flows through a series of middleware functions in the order they're defined. Each function can process the request, modify it, or decide whether to pass it along.

There are five types of middleware: application-level middleware bound to the app object, router-level middleware for modular routing, built-in middleware like express.json(), third-party middleware like cors or helmet, and error-handling middleware with four parameters for centralized error handling.

The key insight is that the order matters - authentication must come before protected routes, and error handlers must be defined last. If middleware doesn't call next() or send a response, the request hangs forever."

The Code Example That Impresses

Draw this on the whiteboard and walk through it:

const express = require('express');
const app = express();
 
// 1. Logging middleware (runs on every request)
app.use((req, res, next) => {
    console.log(`${req.method} ${req.path}`);
    next(); // Pass to next middleware
});
 
// 2. Body parsing middleware
app.use(express.json());
 
// 3. Authentication middleware
const auth = (req, res, next) => {
    const token = req.headers.authorization;
    if (!token) {
        return res.status(401).json({error: 'No token'});
    }
    req.user = verifyToken(token);
    next();
};
 
// 4. Protected route with middleware
app.get('/api/profile', auth, (req, res) => {
    res.json({user: req.user});
});
 
// 5. Error handling middleware (MUST be last)
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({error: 'Something went wrong'});
});

Walk through the flow:

  1. Request arrives at /api/profile
  2. Logging middleware runs, logs the request, calls next()
  3. express.json() parses the body, calls next()
  4. auth middleware checks token - if missing, sends 401 and stops
  5. If token is valid, attaches user to req and calls next()
  6. Route handler sends the response
  7. If any middleware throws an error, error handler catches it

The Classic Problem (They WILL Ask This)

This question separates those who memorized from those who understand:

app.use((req, res, next) => {
    console.log('A');
    next();
});
 
app.use((req, res, next) => {
    console.log('B');
    // Oops, forgot to call next()
});
 
app.get('/test', (req, res) => {
    console.log('C');
    res.send('Hello');
});

What happens when you request /test?

Answer: It logs A, then B, then the request hangs forever. The route handler never runs because the second middleware never calls next(). The client eventually times out.

Fix: Always call next() or send a response.

The Tricky Error Handling Question

app.get('/error', (req, res, next) => {
    throw new Error('Sync error');
});
 
app.get('/async-error', async (req, res, next) => {
    throw new Error('Async error');
});
 
app.use((err, req, res, next) => {
    res.status(500).json({error: err.message});
});

Question: Which route's error gets caught?

Answer: Only the synchronous /error route is caught by the error handler. Async errors in Express 4.x are not automatically caught - they crash the app or cause the request to hang.

The fix (Express 4.x):

app.get('/async-error', async (req, res, next) => {
    try {
        throw new Error('Async error');
    } catch (err) {
        next(err); // Manually pass to error handler
    }
});
 
// Or use a wrapper
const asyncHandler = (fn) => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};
 
app.get('/async-error', asyncHandler(async (req, res) => {
    throw new Error('Async error');
}));

Note: Express 5.x (currently in beta) handles async errors automatically.

Five Types of Middleware (Know All of Them)

1. Application-Level Middleware

// Runs on every request
app.use((req, res, next) => {
    req.requestTime = Date.now();
    next();
});
 
// Runs only on specific path
app.use('/api', (req, res, next) => {
    console.log('API request');
    next();
});

2. Router-Level Middleware

const router = express.Router();
 
router.use((req, res, next) => {
    console.log('Router middleware');
    next();
});
 
router.get('/users', (req, res) => {
    res.json([]);
});
 
app.use('/api', router);

3. 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

4. Third-Party Middleware

const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
 
app.use(cors());          // Enable CORS
app.use(helmet());        // Security headers
app.use(morgan('dev'));   // Request logging

5. Error-Handling Middleware

// Note the four parameters - this is what makes it error middleware
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(err.status || 500).json({
        error: err.message
    });
});

Common Follow-Up Questions

"What's the difference between app.use() and app.get()?"

"app.use() matches all HTTP methods and partial paths. app.use('/api') matches /api, /api/users, /api/anything. app.get() only matches GET requests and exact paths. Use app.use() for middleware, app.get() for specific route handlers."

"How do you pass data between middleware?"

"Attach it to the request object. For example, authentication middleware sets req.user, and subsequent middleware or route handlers can access req.user. This is safer than using global variables or closures."

app.use((req, res, next) => {
    req.customData = {timestamp: Date.now()};
    next();
});
 
app.get('/test', (req, res) => {
    console.log(req.customData); // { timestamp: ... }
    res.send('OK');
});

"How do you skip to the next route?"

"Call next('route'). This skips remaining middleware in the current route and moves to the next matching route. It only works in middleware loaded using app.METHOD() or router.METHOD()."

app.get('/user/:id',
    (req, res, next) => {
        if (req.params.id === '0') {
            return next('route'); // Skip to next route
        }
        next();
    },
    (req, res) => {
        res.send('Regular user');
    }
);
 
app.get('/user/:id', (req, res) => {
    res.send('Special user 0');
});

"Why does middleware order matter?"

"Middleware executes in definition order. If you define routes before express.json(), those routes won't have parsed body data. If you define error handlers before routes, they won't catch route errors. Authentication must come before protected routes. This is a common source of bugs."

// WRONG - route defined before body parser
app.post('/data', (req, res) => {
    console.log(req.body); // undefined!
    res.send('OK');
});
app.use(express.json());
 
// CORRECT
app.use(express.json());
app.post('/data', (req, res) => {
    console.log(req.body); // { ... }
    res.send('OK');
});

What Interviewers Are Really Testing

When I ask about middleware, I'm checking:

  1. Core understanding - Can you explain the request-response cycle?
  2. Practical knowledge - Do you know why order matters and what happens without next()?
  3. Error handling - Do you understand the four-parameter error middleware?
  4. Real-world experience - Can you discuss common middleware like cors, helmet, morgan?
  5. Debugging skills - Can you identify why a request might hang?

A candidate who can draw the middleware flow, explain error handling, and discuss order dependency demonstrates real experience.

Quick Reference Card

ConceptWhat to Remember
Middleware signature(req, res, next) => {}
Error middleware(err, req, res, next) => {} (4 params)
Pass controlCall next()
Pass errorCall next(error)
Skip routeCall next('route')
Request hangs ifNo next() and no response
Order ruleAuth before routes, errors last
Pass dataAttach to req object

Practice Questions

Test yourself before your interview:

1. What's wrong with this code?

app.use((req, res, next) => {
    if (!req.headers.authorization) {
        res.status(401).send('Unauthorized');
    }
    next();
});

2. What's the output?

app.use((req, res, next) => {
    console.log('A');
    next();
});
app.use('/api', (req, res, next) => {
    console.log('B');
    next();
});
app.get('/api/test', (req, res) => {
    console.log('C');
    res.send('OK');
});
 
// Request: GET /api/test

3. Will the error handler catch this error?

app.get('/test', async (req, res) => {
    const data = await fetchData(); // throws error
    res.json(data);
});
 
app.use((err, req, res, next) => {
    res.status(500).json({error: err.message});
});

Answers:

  1. Missing return before res.status(401). Without it, next() is called after sending 401, causing "headers already sent" error.
  2. A, B, C - all middleware runs in order.
  3. No, async errors aren't caught in Express 4.x. Need try/catch or asyncHandler wrapper.

Related Articles

If you found this helpful, check out these related guides:


Ready for More Node.js Interview Questions?

This is just one of 45+ Node.js questions in our complete interview prep guide. Each question includes:

  • Concise answers for time-pressed interviews
  • Code examples you can write on a whiteboard
  • Follow-up questions interviewers actually ask
  • Insights from real interview experience

Get Full Access to All Node.js Questions →

Or try our free preview to see more questions like this.


Written by the EasyInterview team, based on real interview experience from 12+ years in tech and hundreds of technical interviews conducted at companies like BNY Mellon, UBS, and leading fintech firms.

Ready to ace your interview?

Get 550+ interview questions with detailed answers in our comprehensive PDF guides.

View PDF Guides