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
nextfunction. Middleware can execute code, modify the request and response, end the cycle by sending a response, or callnext()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 likecorsorhelmet, 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:
- Request arrives at
/api/profile - Logging middleware runs, logs the request, calls
next() express.json()parses the body, callsnext()authmiddleware checks token - if missing, sends 401 and stops- If token is valid, attaches user to
reqand callsnext() - Route handler sends the response
- 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 files4. 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 logging5. 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. Useapp.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 accessreq.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 usingapp.METHOD()orrouter.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:
- Core understanding - Can you explain the request-response cycle?
- Practical knowledge - Do you know why order matters and what happens without
next()? - Error handling - Do you understand the four-parameter error middleware?
- Real-world experience - Can you discuss common middleware like cors, helmet, morgan?
- 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
| Concept | What to Remember |
|---|---|
| Middleware signature | (req, res, next) => {} |
| Error middleware | (err, req, res, next) => {} (4 params) |
| Pass control | Call next() |
| Pass error | Call next(error) |
| Skip route | Call next('route') |
| Request hangs if | No next() and no response |
| Order rule | Auth before routes, errors last |
| Pass data | Attach 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/test3. 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:
- Missing
returnbeforeres.status(401). Without it,next()is called after sending 401, causing "headers already sent" error. A,B,C- all middleware runs in order.- 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:
- Complete Node.js Backend Developer Interview Guide - comprehensive preparation guide for backend interviews
- NestJS Interview Guide - Guards, pipes, interceptors, and structured middleware patterns
- WebSockets & Socket.IO Interview Guide - Real-time middleware patterns with Socket.IO
- Authentication & JWT Interview Guide - Auth middleware, sessions, JWT, and Passport.js
- REST API Interview Guide - API design principles and best practices
- Node.js Advanced Interview Guide - Event loop, streams, and Node.js internals
- JavaScript Event Loop Interview Guide - How async JavaScript really works under the hood
- Docker Interview Guide - Containerizing Express apps for production
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.
