Express.js Framework Interview Questions for Full Stack with Node.js
25 Express.js Framework questions with answers for full stack with node.js roles — 11 easy, 12 medium, 2 hard.
About Express.js Framework
Learn Express routing, middleware, routers, request and response objects, error handling, validation, file uploads, CORS and security headers. You will structure large projects into routes, controllers, services and repositories, handle async errors correctly, understand what changed in Express 5, and know when frameworks like NestJS or Fastify are a better fit.
Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
Express is a minimal, unopinionated web framework for Node.js that adds routing, middleware and helpers for requests and responses on top of the http module. It is widely used because it is simple, flexible and has a large ecosystem of middleware for logging, auth, validation and security. Many teams also use it as the base for frameworks like NestJS.
Middleware is a function with the signature (req, res, next) that runs during the request-response cycle. It can read or modify req and res, end the response, or call next() to pass control to the next middleware. Typical uses are parsing JSON, logging, authentication, CORS and rate limiting. Order matters, because middleware runs in the sequence it is registered.
03
What is the difference between app.use and app.get?
app.use mounts middleware for all HTTP methods and matches any path that starts with the given prefix, for example app.use('/api', router). app.get registers a handler only for GET requests on an exact route pattern. Use app.use for cross-cutting concerns like parsing and auth, and method-specific functions such as get, post, put and delete for endpoints.
// routes/users.js
const router = require('express').Router();
router.get('/', listUsers);
router.get('/:id', getUser);
router.post('/', auth, createUser);
module.exports = router;
// app.js
app.use(express.json());
app.use('/api/v1/users', require('./routes/users'));
// Keeps each resource in its own module with its own middleware.
05
What is the difference between req.params, req.query and req.body?
req.params holds route parameters from the path, such as id in /users/:id. req.query holds query string values like ?page=2&sort=name, used for filtering and pagination. req.body holds the parsed request payload of POST, PUT or PATCH and needs a parser such as express.json(). All are user input and must be validated before use.
06
How do you enable CORS in an Express app and why is it needed?
Browsers block cross-origin requests unless the server sends CORS headers. Use the cors package: app.use(cors({ origin: ['https://app.example.com'], credentials: true })). Avoid origin '*' with credentials, and restrict methods and headers you allow. CORS is a browser protection, not a security layer for your API, so you still need authentication and authorisation.
helmet is middleware that sets secure HTTP response headers. It adds Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options nosniff, frame protection and removes X-Powered-By, reducing risks like clickjacking, MIME sniffing and some XSS attacks. It is one line, app.use(helmet()), but CSP usually needs tuning so your frontend scripts and assets still load.
08
What is the difference between res.send, res.json and res.end?
res.send sends a response and sets Content-Type based on the argument: string as HTML, Buffer as binary, object as JSON. res.json always serialises to JSON and sets application/json, and respects app settings like json spaces. res.end ends the response with no extra processing, useful for empty responses such as 204. Call only one per request to avoid headers-already-sent errors.
09
What causes the error 'Cannot set headers after they are sent to the client'?
It happens when code tries to send a second response for the same request, for example calling res.json in a callback and then again later, or forgetting return after sending an error response, or calling next() after responding. Fix it by returning immediately after each res.send or res.json, and making sure each code path sends exactly one response.
10
A client reports that a large JSON POST request fails with 413 Payload Too Large. What do you do?
express.json() has a default body limit of 100 KB, so bigger payloads are rejected with 413. If the size is legitimate, raise it for that route only, for example express.json({ limit: '2mb' }), and check any proxy limit such as Nginx client_max_body_size. For very large data, switch to file upload to object storage or pagination instead of huge JSON bodies.
11
Describe a time you disagreed with a senior developer on a technical approach.
Situation: a senior wanted to store sessions in server memory for a new API. Task: I believed it would break when we scaled to multiple pods. Action: I prepared a short comparison, showed a demo where users were logged out after a restart, and suggested Redis sessions or JWT. Result: we agreed on Redis, and the senior asked me to document the decision for the team.
12
How does error-handling middleware work in Express?
Error-handling middleware has four parameters (err, req, res, next) and must be registered after all routes. When any middleware calls next(err) or throws in a synchronous handler, Express skips normal middleware and jumps to the error handler. In Express 4, async errors must be passed to next manually or with a wrapper; Express 5 forwards rejected promises automatically.
13
Write a centralised error handler with a custom AppError class.
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
router.get('/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new AppError('Not found', 404);
res.json(user);
}));
// Without this, a rejected promise is never sent to the error handler.
// Express 5 does this automatically.
15
Write a simple request logging middleware that records response time.
function requestLogger(req, res, next) {
const start = process.hrtime.bigint();
res.on('finish', () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${ms.toFixed(1)}ms`);
});
next();
}
app.use(requestLogger);
// In production use pino-http or morgan with structured logs.
Use multer for multipart/form-data. Configure limits for file size and count, filter allowed MIME types and extensions, and store files with generated names, not user-supplied names. For production, stream uploads directly to object storage such as S3 using presigned URLs so files do not pass through or fill the server disk. Scan files and never execute uploaded content.
I use a layered structure: routes define endpoints, controllers handle HTTP details, services hold business logic, and repositories or models talk to the database. Add middlewares, validators, config and utils folders, and group by feature, for example modules/orders, once the app grows. This keeps controllers thin, makes services unit-testable and avoids business rules leaking into routes.
A hang usually means the middleware never calls next() or sends a response on some path. A 404 often means it was mounted with a wrong path or registered after routes. I check order of app.use calls, add logging at the start and end of the middleware, verify every branch calls next or responds, and check async code passes errors to next.
20
When would you choose NestJS or Fastify over Express?
Choose Fastify when raw performance and built-in schema validation matter; it is faster than Express thanks to optimised routing and serialisation. Choose NestJS for large teams that want structure: modules, dependency injection, decorators and first-class TypeScript, similar to Spring or Angular. Express still suits small to medium services and teams that prefer flexibility and a huge middleware ecosystem.
21
How do you serve a React build and an API from the same Express server?
const path = require('path');
app.use('/api', apiRouter);
app.use(express.static(path.join(__dirname, 'client/dist'), { maxAge: '1y' }));
// SPA fallback for client-side routes
app.get(/^\/(?!api).*/, (req, res) => {
res.sendFile(path.join(__dirname, 'client/dist/index.html'));
});
// Register API routes first so they are not caught by the fallback.
22
How do you design Express middleware that checks user roles for a route?
Create a factory: const authorize = (...roles) => (req, res, next) => roles.includes(req.user?.role) ? next() : res.status(403).json({ error: 'Forbidden' }). Use it after authentication, for example router.delete('/:id', authenticate, authorize('admin'), handler). Keep roles and permissions in config or the database, and for complex rules move to permission-based checks or a policy library.
23
Tell me about a time you improved code quality in a backend codebase you inherited.
Situation: I joined a project where Express routes contained SQL, business logic and responses in 300-line handlers. Task: reduce bugs without stopping feature work. Action: I proposed a controller-service-repository structure, added an error handler, validation with zod and tests for each refactored route, and moved one module per sprint. Result: bug reports dropped and new developers onboarded faster.
Express 5, now the stable line, forwards rejected promises from async handlers to error middleware automatically. It updates path-to-regexp, so route patterns are stricter: wildcards must be named and some regex characters are no longer allowed in strings. Deprecated methods such as res.send(status) and req.param() were removed, and it requires a modern Node.js version. Upgrade by running tests and checking route patterns.
25
Your Express API needs to support multiple client apps with different rate limits. How would you design it?
Identify the client through an API key or OAuth client ID in authentication middleware and attach the plan to req. Then use express-rate-limit or a custom limiter with a Redis store, keyed by client ID, with limits read from the plan. Return 429 with Retry-After and RateLimit headers. Redis keeps limits consistent across multiple server instances.
Frequently asked questions
How important is Express.js Framework in Full Stack with Node.js interviews?
Learn Express routing, middleware, routers, request and response objects, error handling, validation, file uploads, CORS and security headers. You will structure large projects into routes, controllers, services and repositories, handle async errors correctly, understand what changed in Express 5, and know when frameworks like NestJS or Fastify are a better fit.
How should I use the Express.js Framework sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.