Skip to content

Scenario-Based MERN Stack Interview Questions

47 questions with answers. Real-work situations — broken dashboards, flaky tests, unclear requirements — and how a strong mern stack developer would handle them.

How to approach these questions

Clarify the situation, list likely causes, say how you would check each one, then give the fix and how you would stop it happening again.

Solved — scenario-basedStart 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 the output order of console.log('A'), setTimeout(() => console.log('B'), 0), Promise.resolve().then(() => console.log('C')) and console.log('D')?

Save to ▾
Show answer
The output is A, D, C, B. A and D are synchronous and run immediately on the call stack. The Promise callback C goes into the microtask queue, and the setTimeout callback B goes into the macrotask queue. After the synchronous code finishes, the event loop drains all microtasks first, printing C, and only then runs the timer callback, printing B, even with a 0 ms delay.
02

A for loop using var with setTimeout inside prints 3, 3, 3 instead of 0, 1, 2. Why, and how do you fix it?

Save to ▾
Show answer
var is function-scoped, so all three callbacks close over the same variable, which has become 3 by the time the timers fire. The simplest fix is to declare the loop variable with let, because let creates a fresh binding for every iteration. Older fixes include wrapping the body in an immediately invoked function or passing i as the third argument to setTimeout.
03

A search box fires an API request on every keystroke and overloads the backend. How would you solve it?

Save to ▾
Show answer
Debounce the input so the request goes out only after the user stops typing for around 300 ms. Also skip very short queries, cancel the previous request with AbortController so stale responses cannot overwrite newer ones, and cache recent results. On the server side, add rate limiting and an index on the searched field. Throttling is the alternative when you need periodic updates, like scroll tracking.
04

You need to call an external API 500 times but it allows only 5 concurrent requests. How would you implement this?

Save to ▾
Show answer
Use a concurrency-limited queue. Keep a pool of five workers; each worker picks the next item from a shared index, awaits the request, and loops until the list is exhausted. Run the workers with Promise.all. Libraries such as p-limit do this neatly. Add retries with backoff for 429 responses and collect failures separately instead of failing the whole batch, so one bad item does not stop the job.
05

A single-page app gets slower the longer a user keeps it open. What JavaScript memory leaks would you check for?

Save to ▾
Show answer
Look for event listeners, intervals and subscriptions added without cleanup, closures holding large objects, detached DOM nodes still referenced in variables, growing caches or global arrays, and WebSocket handlers registered on every render. In React, these usually come from useEffect without a cleanup function. Use the Chrome DevTools Memory tab to take heap snapshots before and after an action and compare retained objects.
06

Your API becomes unresponsive whenever a user uploads an image for resizing. What is happening and how do you fix it?

Save to ▾
Show answer
Resizing is CPU-bound work running on the main thread, so it blocks the event loop and every other request waits. Fix it by moving the work off the main thread: use a worker_threads pool, a native library like sharp that works in the libuv pool, or better, push a job to a queue such as BullMQ with Redis and process it in a separate worker service, then notify the client.
07

Your script crashes with out of memory while reading a 5 GB log file. How would you rewrite it?

Save to ▾
Show answer
fs.readFile loads the whole file into memory, which exceeds the heap. Use a stream instead: fs.createReadStream with readline lets you process one line at a time with for await, keeping memory flat. If you need to transform and write output, connect read, transform and write streams with pipeline so backpressure and errors are handled. Increasing --max-old-space-size only hides the real problem.
08

Memory usage of a production Node.js service keeps rising until it restarts. How do you investigate?

Save to ▾
Show answer
First confirm the trend with monitoring of heapUsed and RSS. Reproduce in staging under load, then take heap snapshots with --inspect and Chrome DevTools, or use --heapsnapshot-signal, and compare snapshots to find growing object types and their retainers. Common causes are unbounded in-memory caches, listeners added per request, global arrays and unclosed timers. Fix the root cause, add a bounded LRU cache, and set alerts.
09

How do you implement graceful shutdown for a Node.js API during deployments?

Save to ▾
Show answer
Listen for SIGTERM and SIGINT. When received, stop accepting new connections with server.close(), let in-flight requests finish, close database connections with mongoose.connection.close() and other resources, then exit with code 0. Set a timeout, for example 10 seconds, after which you force exit. In Kubernetes or behind a load balancer, also fail the readiness check first so traffic stops reaching the instance.
10

How do you manage configuration and secrets differently for development and production in Node.js?

Save to ▾
Show answer
Keep config in environment variables, following the twelve-factor approach. Locally, load a .env file with dotenv or Node's built-in --env-file flag, and add .env to .gitignore while committing a .env.example. In production, inject variables through the hosting platform or a secrets manager. Validate all required variables at startup with a schema library like zod so the app fails fast with a clear message.
11

Your Node.js API runs on an 8-core server but CPU monitoring shows only one core busy. What do you do?

Save to ▾
Show answer
A Node process runs JavaScript on a single thread, so one instance uses about one core. Run multiple instances: PM2 in cluster mode with -i max, the cluster module, or several containers behind a load balancer such as Nginx. Because instances do not share memory, move sessions, caches and rate-limit counters to Redis, and make sure the app is stateless so any instance can serve any request.
12

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

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

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

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

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

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

A MongoDB query on an orders collection takes 3 seconds. How do you diagnose and fix it?

Save to ▾
Show answer
Run the query with .explain('executionStats') and check whether the winning plan is COLLSCAN and how totalDocsExamined compares to nReturned. Create an index matching the filter and sort, following ESR, and confirm it becomes IXSCAN. Also project only needed fields, add limits, use lean in Mongoose, and check the profiler or Atlas Performance Advisor for other slow queries. Re-test with production-like data volume.
19

You stored all comments inside each blog post document and some posts are now very large. How would you redesign it?

Save to ▾
Show answer
An unbounded embedded array risks hitting the 16 MB document limit and makes every post read and update heavy. Move comments to their own collection with a postId field and an index on { postId: 1, createdAt: -1 }, then paginate. Optionally keep a small embedded array of the latest few comments plus a commentCount on the post for fast first render. This is the subset pattern.
20

Duplicate user accounts with the same email are being created even though the code checks for an existing email first. Why, and how do you fix it?

Save to ▾
Show answer
Check-then-insert is a race condition: two concurrent requests both see no user and both insert. The real fix is a unique index on email, normalised to lowercase, so the database rejects the second insert with error code 11000. Catch that error and return 409 Conflict. Clean up existing duplicates before building the index, otherwise index creation fails.
21

The business wants total sales per category for the last 30 days. How would you build it in MongoDB?

Save to ▾
Show answer
Use an aggregation pipeline: $match orders with status paid and createdAt in the last 30 days, using an index on createdAt; $unwind the items array; $group by items.category summing items.price times items.qty; then $sort by total descending. If the report is viewed often, precompute daily totals into a summary collection with a scheduled job or $merge, so the dashboard reads small documents.
22

A client wants deleted records to be recoverable. How would you implement soft delete with Mongoose?

Save to ▾
Show answer
Add fields like isDeleted: Boolean with default false and deletedAt: Date. The delete endpoint sets these instead of removing the document. Add a pre-find query middleware that filters isDeleted: false by default, and an admin route to restore. Include isDeleted in relevant indexes or use partial indexes, for example a unique email index that only applies to non-deleted users. Purge old records with a scheduled job.
23

You need to rename a field and change its type across 20 million documents in production. How do you do it safely?

Save to ▾
Show answer
Avoid a single big blocking update. Make the application read both old and new formats first, then run a batched migration script that processes documents in chunks by _id range with bulkWrite, throttled and resumable, or an updateMany with an aggregation pipeline during low traffic. Track progress, verify counts, back up first, then remove the old-field handling in a later release.
24

Two sibling components need to share data. How do you handle it?

Save to ▾
Show answer
Lift the shared state to their nearest common parent and pass the value to one sibling and an updater to the other. If the data is needed by many components deep in the tree, use Context or a state library like Zustand or Redux Toolkit. If the data comes from the server, a data-fetching library like TanStack Query can share the cached result across components.
25

Every time a user types in an input inside a list item, the input loses focus. What could be the cause?

Save to ▾
Show answer
The component is likely being remounted on each render. Common causes are generating keys randomly, such as key={Math.random()}, defining a component inside another component's body so it gets a new identity every render, or using index keys while the list reorders. Use stable ids as keys and move inner component definitions to the module level so React preserves their DOM and state.
26

You are building a registration form with ten fields and validation. How do you manage its state?

Save to ▾
Show answer
For a simple form, keep a single state object and update fields with a shared handler using the input name, for example setForm({ ...form, [e.target.name]: e.target.value }). Validate on blur and submit, storing errors in another object. For larger forms, use React Hook Form with a zod schema, which reduces re-renders and shares the validation schema with the Express backend.
27

How do you protect certain routes in React so that only logged-in users can access them?

Save to ▾
Show answer
Create a ProtectedRoute or layout component that reads auth state from context. If the user is not authenticated, return <Navigate to='/login' replace state={{ from: location }} />, otherwise render the Outlet or children. Show a loader while the session check is pending to avoid flicker. Remember that this is only for user experience; real protection must happen on the Express API by verifying tokens on every request.
28

A component calls setState inside useEffect and the app goes into an infinite render loop. Why?

Save to ▾
Show answer
The effect updates state, which re-renders, which runs the effect again. This happens when the dependency array is missing, or when a dependency is a new object or array created every render, or when the effect sets the same state it depends on. Add correct dependencies, move object creation inside the effect or memoize it, and ask whether the value can be calculated during render instead.
29

How should a component display data loaded from an API in a user-friendly way?

Save to ▾
Show answer
Handle every state explicitly: show a skeleton or spinner while loading, an error message with a retry button if the request fails, an empty state with guidance when there is no data, and the content otherwise. Keep these in state variables or use a library like TanStack Query that provides isPending, isError and data. Never leave a blank screen or crash on undefined data.
30

Typing in a filter box over a list of 10,000 rows feels laggy. How do you fix it?

Save to ▾
Show answer
First profile to confirm what re-renders. Then virtualise the list with a library like TanStack Virtual or react-window so only visible rows render. Memoise row components and keep the filtered list in useMemo. Use useDeferredValue or startTransition so the input stays responsive while the list update is treated as low priority. For very large data, filter on the server with pagination and an index.
31

A setInterval inside useEffect always logs the initial count value instead of the updated one. Why, and how do you fix it?

Save to ▾
Show answer
This is a stale closure: the effect ran once with an empty dependency array, so its callback captured count from the first render forever. Fixes include using the functional updater setCount((c) => c + 1) so you do not need to read count, adding count to the dependencies with proper cleanup, or storing the latest value in a ref that the interval reads.
32

Props are being passed through five levels of components that do not use them. How would you refactor?

Save to ▾
Show answer
This is prop drilling. First try component composition: pass ready-made elements as children or props so intermediate components do not need the data. If many distant components need it, create a Context with a custom hook like useAuth. For complex or frequently updated shared state, use a store such as Zustand or Redux Toolkit with selectors so only interested components re-render.
33

An error in one dashboard widget crashes the entire React page. How do you contain it?

Save to ▾
Show answer
Wrap risky sections in error boundaries. An error boundary catches rendering errors in its subtree and shows fallback UI instead of unmounting the whole app. React still requires a class with getDerivedStateFromError and componentDidCatch, or you can use the react-error-boundary package. Place boundaries around each widget and the route level, log errors to monitoring, and offer a retry. They do not catch errors in event handlers or async code.
34

Your React production bundle is 3 MB and the first load is slow on mobile. What steps do you take?

Save to ▾
Show answer
Analyse the bundle to find the largest modules. Split code by route with React.lazy, lazy load heavy libraries like charts and editors, replace large dependencies with lighter ones, import only needed functions so tree shaking works, and remove unused polyfills. Compress images, serve assets from a CDN with Brotli and long cache headers, and consider SSR or static rendering for landing pages. Track Core Web Vitals after each change.
35

Fast typing in a search box sometimes shows results for an older query. What causes this and how do you fix it?

Save to ▾
Show answer
It is a race condition: responses arrive out of order, so a slow earlier request overwrites results of a newer one. In the effect, use an AbortController and abort the previous request in the cleanup, or keep an ignore flag set to true during cleanup and skip setState when it is set. Debouncing reduces requests, and TanStack Query handles this automatically by keying results to the query.
36

When a user logs out or changes password, how do you make sure their existing JWTs stop working?

Save to ▾
Show answer
Stateless JWTs stay valid until expiry, so keep access tokens short-lived and revoke refresh tokens by deleting or flagging them in the database. For immediate invalidation, store a tokenVersion or passwordChangedAt on the user and include it in the token; the auth middleware rejects tokens with an older version. Alternatively keep a Redis denylist of token ids until they expire. Clear the refresh cookie on logout.
37

Your app needs user, editor and admin roles with different permissions. How would you implement it?

Save to ▾
Show answer
Store a role, or a list of permissions, on the user document and include it in the token or load it in the auth middleware. Write an authorize middleware that checks allowed roles or permissions per route, for example authorize('admin'). Add ownership checks in services, such as an editor editing only their own articles. In React, hide buttons by role for user experience, but always enforce rules on the server.
38

A developer accidentally pushed the .env file with the MongoDB password and JWT secret to a public repository. What do you do?

Save to ▾
Show answer
Treat the secrets as compromised immediately. Rotate the MongoDB user password, the JWT secret and any API keys; rotating the JWT secret logs everyone out, which is acceptable. Check database and cloud logs for suspicious access. Remove the file, add .env to .gitignore, and purge it from history with a tool like git filter-repo, though rotation matters more. Enable secret scanning and pre-commit hooks to prevent repeats.
39

Attackers are trying thousands of passwords on your login endpoint. How do you defend it?

Save to ▾
Show answer
Apply strict rate limits on login per IP and per account using a shared Redis store, add progressive delays or temporary account lockout after several failures, and show a CAPTCHA after repeated attempts. Return a generic error message so attackers cannot tell whether an email exists. Log and alert on spikes, check for breached passwords at signup, and offer two-factor authentication for sensitive accounts.
40

Users can upload profile pictures and documents. What security measures do you put in place?

Save to ▾
Show answer
Enforce size limits and an allowlist of types, checking magic bytes rather than trusting the extension or Content-Type. Rename files with random ids, store them in object storage outside the web root, and serve them from a separate domain or with Content-Disposition attachment for documents. Strip image metadata, scan for malware where needed, use pre-signed URLs with expiry for private files, and never execute or include uploaded files.
41

Design a secure forgot-password flow for a MERN application.

Save to ▾
Show answer
The user submits an email; always respond with the same message whether or not it exists. Generate a random token with crypto.randomBytes, store only its hash with an expiry of around 15 minutes, and email a link with the raw token. On submission, hash the token, find a matching unexpired record, set the new hashed password, delete the token and invalidate existing sessions. Rate limit the endpoint.
42

Your MERN app works on your laptop but crashes after deployment to a server. How do you troubleshoot?

Save to ▾
Show answer
Read the server logs first. Common causes are missing environment variables, a different Node version, the database host or IP allowlist blocking the server, hardcoded localhost URLs, wrong PORT handling, devDependencies missing in production, case-sensitive file names on Linux, and the frontend API base URL pointing to localhost. Reproduce with the same Node version or a Docker image, and validate config at startup.
43

After deploying a React app, refreshing on a page like /dashboard shows a 404 error. Why, and how do you fix it?

Save to ▾
Show answer
With client-side routing, /dashboard exists only inside React Router, not as a file on the server. When the browser requests it directly, the web server finds no file and returns 404. Configure the server to fall back to index.html for unknown paths: try_files $uri /index.html in Nginx, a rewrite rule on static hosts, or a catch-all route in Express that serves index.html after the API routes.
44

Your MERN product is expected to grow from 1,000 to 1 lakh daily users. How would you prepare the architecture?

Save to ▾
Show answer
Make the API stateless and run multiple instances behind a load balancer with autoscaling. Serve the React build and media from a CDN. Move sessions, rate limits and caches to Redis. Optimise MongoDB with proper indexes, a replica set, read preferences for analytics and plan sharding if data grows. Push slow work to queues, add monitoring and alerts, and load test before launch to find limits.
45

How would you achieve zero-downtime deployments for an Express API?

Save to ▾
Show answer
Run at least two instances behind a load balancer and use rolling or blue-green deployments: start new instances, wait for health and readiness checks to pass, shift traffic, then drain and stop old instances with graceful shutdown. PM2 reload or Kubernetes rolling updates handle this. Keep database migrations backward compatible, adding fields before code uses them, so old and new versions can run together.
46

Some tests pass locally but fail randomly in CI. How do you handle flaky tests?

Save to ▾
Show answer
Rerun to confirm flakiness, then find the cause instead of adding retries. Common causes are shared database state between tests, tests depending on run order, real timers and dates, network calls to real services, and E2E tests using fixed waits instead of waiting for elements. Isolate data per test, mock time and external APIs, use proper async waits, and quarantine a flaky test with a ticket until fixed.
47

Your chat feature uses Socket.IO and must now run on multiple Node instances. What changes are needed?

Save to ▾
Show answer
Each instance only knows its own connected sockets, so a message emitted on one would not reach users on another. Add the Socket.IO Redis adapter, or a similar pub/sub adapter, so events are broadcast across instances. Enable sticky sessions at the load balancer if HTTP long-polling fallback is used. Store presence and room membership in Redis, persist messages in MongoDB, and authenticate sockets with tokens during the handshake.

Frequently asked questions

Why are scenario-based questions important?

They show how you think on real problems. Interviewers use them to separate people who memorised definitions from people who have done the work.

How should I answer them?

Clarify the situation, list likely causes, say how you would check each one, then give the fix and how you would stop it happening again.