Skip to content

Express.js and REST APIs Interview Questions for MERN Stack

25 Express.js and REST APIs questions with answers for mern stack roles — 8 easy, 14 medium, 3 hard.

About Express.js and REST APIs

Express is the most popular web framework for Node.js and the E in MERN. This topic covers middleware, routing, request data, error handling, CORS, validation, status codes, pagination, versioning and the changes in Express 5. Questions focus on designing clean, secure and consistent REST APIs that a React frontend and mobile apps can rely on in production.

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.
3 questions free Unlock the rest in seconds
01

What is Express.js and what is middleware?

Save to ▾
Show answer
Express is a minimal web framework for Node.js that adds routing, middleware and helpers on top of the http module. Middleware is a function with the signature (req, res, next) that runs during the request cycle. It can read or modify the request, end the response, or call next() to pass control on. Body parsing, logging, authentication, CORS and error handling are all built as middleware.
02

Why does the order of middleware matter in Express?

Save to ▾
Show answer
Express runs middleware and routes in the order they are registered. If express.json() is added after your routes, req.body will be undefined. Authentication middleware must come before protected routes, static file serving usually comes early, a 404 handler comes after all routes, and the error-handling middleware must be registered last. If a middleware neither sends a response nor calls next(), the request hangs.
03

What is the difference between app.use, app.get and express.Router?

Save to ▾
Show answer
app.use mounts middleware for all HTTP methods on a path prefix, so app.use('/api', fn) runs for /api/anything. app.get registers a handler only for GET requests matching that exact path pattern. express.Router creates a mini application with its own routes and middleware, which you mount with app.use('/api/users', userRouter). Routers keep large projects modular, with one file per resource.
04

What is the difference between req.params, req.query and req.body?

Save to ▾
Show answer
req.params holds named route segments, so for /users/:id a request to /users/42 gives req.params.id equal to '42'. req.query holds the query string, so ?page=2&sort=name gives { page: '2', sort: 'name' }. req.body holds the parsed request payload, available only after a parser like express.json(). Params and query values are strings, so convert and validate them before use.
05

Which HTTP status codes should a REST API commonly return?

Save to ▾
Show answer
Use 200 OK for successful reads and updates, 201 Created after a POST that creates a resource, and 204 No Content for successful deletes. Use 400 for invalid input, 401 when authentication is missing or invalid, 403 when the user is authenticated but not allowed, 404 for missing resources, 409 for conflicts such as duplicate email, 422 for validation errors, 429 for rate limits and 500 for unexpected server errors.
06

The React app works locally but shows CORS errors after deployment. What would you check?

Save to ▾
Show answer
Check that the production frontend origin, including https and exact subdomain, is in the API's allowed origins list, and that no trailing slash mismatch exists. If cookies are sent, the server needs credentials: true with a specific origin, not *, and the client needs credentials: 'include' or withCredentials. Also confirm the preflight OPTIONS request is not blocked by a proxy or auth middleware returning 401.
07

Write an Express router with CRUD routes for a products resource.

Save to ▾
Show answer
Keep routes thin and delegate to a controller. import { Router } from 'express'; import * as ctrl from '../controllers/product.controller.js'; const router = Router(); router.get('/', ctrl.list); router.get('/:id', ctrl.getById); router.post('/', ctrl.create); router.patch('/:id', ctrl.update); router.delete('/:id', ctrl.remove); export default router; // app.js app.use(express.json()); app.use('/api/v1/products', router);
08

How would you explain a technical decision, such as choosing REST over GraphQL, to a non-technical manager?

Save to ▾
Show answer
Situation: my manager asked why we were not using GraphQL like a competitor. Task: explain without jargon. Action: I compared it to ordering from a fixed menu versus a custom kitchen, explained that our screens need simple, predictable data, and that REST meant faster delivery with our team's current skills, lower risk and easier caching. Result: the manager approved the plan and we revisited the idea later.
09

How does error-handling middleware work in Express, and what changed in Express 5?

Save to ▾
Show answer
Error handlers have four parameters, (err, req, res, next), and Express routes errors to them when you call next(err) or throw synchronously. In Express 4, rejected Promises in async handlers were not caught, so you needed try/catch or a wrapper. Express 5 automatically forwards rejected Promises from async handlers to the error middleware. Register the error handler after all routes and avoid leaking stack traces in production.
10

What are the core principles of a RESTful API, and which HTTP methods are idempotent?

Save to ▾
Show answer
REST models data as resources identified by URLs, uses standard HTTP methods, is stateless, and returns representations like JSON with proper status codes. Use nouns, for example GET /orders and POST /orders. GET, PUT, DELETE, HEAD and OPTIONS are idempotent: repeating them gives the same server state. POST is not idempotent, and PATCH usually is not, which matters when clients retry requests.
11

What is CORS and how do you configure it in an Express API?

Save to ▾
Show answer
CORS is a browser security mechanism that blocks a page from reading responses from a different origin unless the server allows it through headers like Access-Control-Allow-Origin. For non-simple requests the browser sends a preflight OPTIONS request first. In Express, use the cors package with an explicit allowlist, for example cors({ origin: ['https://app.example.com'], credentials: true }), instead of allowing every origin with credentials.
12

What strategies can you use to version a REST API?

Save to ▾
Show answer
The most common approach is URL versioning such as /api/v1/orders, which is simple and cache friendly. Alternatives are a custom header or a media type like Accept: application/vnd.app.v2+json, which keeps URLs clean but is harder to test in a browser. Whatever you choose, make additive changes without a new version, announce deprecations, and run old and new versions side by side during migration.
13

What are the important changes in Express 5 compared to Express 4?

Save to ▾
Show answer
Express 5 requires a modern Node version and forwards rejected Promises from async handlers to error middleware. Route path syntax moved to a newer path-to-regexp, so wildcards must be named, like /*splat, and regex-style characters in strings are no longer supported. Deprecated methods like res.send(status, body) were removed, req.query is read-only, and req.body is undefined unless a body parser runs.
14

Users must upload files up to 200 MB. How would you handle this in a MERN application?

Save to ▾
Show answer
Avoid buffering big files in the API server's memory. The best approach is to have the backend generate a pre-signed URL for object storage such as S3, then let the React client upload directly to storage, with multipart upload for large files. If uploads must go through Express, use multer with disk storage or stream to storage, enforce size and type limits, and save only metadata in MongoDB.
15

How do you protect a public Express API from abuse and brute-force traffic?

Save to ▾
Show answer
Add rate limiting with express-rate-limit, using a Redis store when running multiple instances, and apply stricter limits to login and OTP routes. Set request body size limits, validate all input, require authentication on non-public routes, add helmet headers and timeouts. At the edge, a CDN or web application firewall can block bots and DDoS traffic before it reaches Node. Return 429 with a Retry-After header.
16

Your API has 40 routes and each returns errors in a different format. How would you standardise this?

Save to ▾
Show answer
Create a custom AppError class with statusCode and code fields, throw it from services and controllers, and handle every error in one central error middleware that returns a consistent shape such as { success: false, error: { code, message, details } }. Map validation, Mongoose cast and duplicate key errors to proper status codes there. Document the format in OpenAPI so the React team handles errors uniformly.
17

Write Express middleware that logs method, URL, status code and response time.

Save to ▾
Show answer
Hook into the response finish event to know the final status. 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 a structured logger like pino instead of console.log.
18

Write a reusable validation middleware for Express using zod.

Save to ▾
Show answer
Validate before the controller runs and return 400 on failure. import { z } from 'zod'; const validate = (schema) => (req, res, next) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(400).json({ errors: result.error.issues }); } req.body = result.data; next(); }; const userSchema = z.object({ name: z.string().min(2), email: z.string().email() }); router.post('/users', validate(userSchema), createUser);
19

Write an asyncHandler wrapper for Express 4 route handlers.

Save to ▾
Show answer
It catches rejected Promises and forwards them to error middleware. 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) return res.status(404).json({ message: 'Not found' }); res.json(user); })); Express 5 does this automatically, but many existing codebases still use this pattern.
20

Write a central error-handling middleware with a custom AppError class.

Save to ▾
Show answer
One handler keeps responses consistent. class AppError extends Error { constructor(message, statusCode = 500) { super(message); this.statusCode = statusCode; this.isOperational = true; } } function errorHandler(err, req, res, next) { if (err.code === 11000) err = new AppError('Duplicate value', 409); const status = err.statusCode || 500; res.status(status).json({ message: err.isOperational ? err.message : 'Something went wrong' }); } app.use(errorHandler); // register last
21

How would you structure the folders of a production Express API?

Save to ▾
Show answer
A layered structure keeps responsibilities clear: src/routes for route definitions, src/controllers for request and response handling, src/services for business logic, src/models for Mongoose schemas, src/middlewares for auth, validation and errors, src/config for environment and database setup, src/utils for helpers, and tests alongside or in a tests folder. Keep app.js, which builds the app, separate from server.js, which listens, so tests can import the app.
22

Tell me about a time you disagreed with a teammate about an API design decision.

Save to ▾
Show answer
Situation: a frontend teammate wanted one endpoint returning the entire dashboard, while I preferred separate resource endpoints. Task: we needed a decision before sprint planning. Action: I listed trade-offs, measured payload size and call counts for both, and we agreed on resource endpoints plus one aggregated summary endpoint for the first screen. Result: the dashboard loaded faster and the API stayed reusable for the mobile app.
23

Compare offset-based and cursor-based pagination for an API.

Save to ▾
Show answer
Offset pagination uses page and limit, translating to skip and limit in MongoDB. It is simple and allows jumping to page 10, but gets slower on large collections and can skip or repeat items when data changes. Cursor pagination returns items after a stable sorted value, such as the last _id or createdAt, using an indexed range query. It stays fast and consistent, ideal for infinite scroll feeds.
24

Your Express API slows down badly during peak traffic. How do you find and fix the bottleneck?

Save to ▾
Show answer
Measure first: add request timing logs or APM tracing to find slow endpoints, then check whether time goes to the database, external calls or CPU. Use explain() for slow queries and add indexes, cache hot read endpoints in Redis, enable compression, paginate large responses and remove synchronous code from handlers. Then scale horizontally with multiple instances behind a load balancer and load test with k6 or autocannon.
25

Design a payment endpoint so that client retries never charge a user twice.

Save to ▾
Show answer
Make the endpoint idempotent with an idempotency key. The client generates a unique key per checkout and sends it in a header. The server stores the key with a unique index along with the request hash and the result. If the same key arrives again, return the stored response instead of reprocessing. Use a status field for in-progress requests and verify final status through the payment gateway webhook.

Frequently asked questions

How important is Express.js and REST APIs in MERN Stack interviews?

Express is the most popular web framework for Node.js and the E in MERN. This topic covers middleware, routing, request data, error handling, CORS, validation, status codes, pagination, versioning and the changes in Express 5. Questions focus on designing clean, secure and consistent REST APIs that a React frontend and mobile apps can rely on in production.

How should I use the Express.js and REST APIs sheet?

Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.