Skip to content

MERN Stack Interview Questions for 7 Years Experience

40 questions with answers for candidates with 7 years of experience — 1 easy, 17 medium, 22 hard.

What interviewers expect: 7 years experience

With seven years, you are assessed as a lead or staff engineer. Expect deep system design across services, data modelling for high scale, multi-region availability, cost optimisation and choosing between monolith and microservices. Interviewers want evidence of technical vision, setting engineering standards, improving delivery processes and working with product and business stakeholders on roadmaps and trade-offs.

This set is weighted towards React Hooks, State and Performance (9), Testing, Deployment and DevOps (7), MongoDB and Mongoose (6), Authentication and Security (6).

Distributed system design Engineering standards Technical roadmap Stakeholder management
Solved at this levelStart 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

Create an AuthContext with a provider and a custom hook.

Save to ▾
Show answer
A provider plus a safe hook: const AuthContext = createContext(null); export function AuthProvider({ children }) { const [user, setUser] = useState(null); const login = async (creds) => { const res = await api.post('/auth/login', creds); setUser(res.data.user); }; const logout = () => setUser(null); const value = useMemo(() => ({ user, login, logout }), [user]); return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth must be used inside AuthProvider'); return ctx; }
02

What is the difference between a shallow copy and a deep copy?

Save to ▾
Show answer
A shallow copy duplicates only the top level, so nested objects are still shared references. Spread syntax and Object.assign make shallow copies. A deep copy duplicates every nested level. structuredClone(obj) is the built-in way in modern browsers and Node.js and supports Dates, Maps and Sets, while JSON.parse(JSON.stringify(obj)) loses functions, undefined and Dates. In React state updates, shallow copies of each changed level are usually enough.
03

How is Nginx used in front of a MERN application?

Save to ▾
Show answer
Nginx acts as a reverse proxy and web server. It serves the built React static files efficiently with caching and compression, proxies /api requests to Node instances, load balances across them, terminates HTTPS with certificates such as Let's Encrypt, and can apply rate limits and request size limits. For React Router, it uses try_files with a fallback to index.html so deep links work.
04

How do transactions work in MongoDB and when do you need them?

Save to ▾
Show answer
Single-document writes are already atomic, so good embedded design avoids most transactions. Multi-document ACID transactions are available on replica sets and sharded clusters, not on a standalone server. In Mongoose, start a session, call session.withTransaction() and pass the session to every operation. Use them for cases like transferring wallet balance between two users. They add latency and should stay short, ideally under one second.
05

How do you debug a JavaScript bug that a user reports but you cannot reproduce locally?

Save to ▾
Show answer
Situation: a user reported a checkout button doing nothing, but it worked for me. Task: find the cause quickly. Action: I asked for browser, device and steps, checked our error-tracking logs, and found a TypeError only on an older Safari version due to an unsupported method. I reproduced it on a cloud device, added a polyfill and a test. Result: the fix shipped the same day and I added browser targets to our build.
06

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

What is PM2 and why is it used to run Node.js in production?

Save to ▾
Show answer
PM2 is a process manager for Node.js. It keeps the app alive by restarting it after crashes, runs multiple instances in cluster mode to use all CPU cores, supports zero-downtime reloads with pm2 reload, manages logs and starts apps on server boot. Configuration lives in an ecosystem.config.js file. On container platforms like Kubernetes, the orchestrator often replaces PM2's restart and scaling role.
08

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

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

What is the EventEmitter class and where is it used?

Save to ▾
Show answer
EventEmitter from the node:events module implements the publish-subscribe pattern. You register listeners with on or once and trigger them with emit, passing data. Many core objects extend it, including HTTP servers, streams and process. Always handle the error event, because an emitted error with no listener crashes the process. Remove listeners you no longer need to avoid the MaxListenersExceededWarning and memory leaks.
11

How does the useEffect dependency array work and why is cleanup important?

Save to ▾
Show answer
useEffect runs after render. With no array it runs after every render, with an empty array only after mount, and with dependencies whenever any of them change by Object.is comparison. The function you return is the cleanup, which runs before the next effect and on unmount. Use it to remove listeners, clear intervals, abort requests and close sockets, otherwise you get leaks and duplicate subscriptions.
12

What are replica sets and sharding in MongoDB, and how do you choose a shard key?

Save to ▾
Show answer
A replica set is a primary plus secondaries holding copies of data, giving automatic failover and optional read scaling. Sharding splits a collection across multiple shards for horizontal scaling of data size and write throughput. A good shard key has high cardinality, evenly distributed writes and matches common queries so they target one shard. Monotonic keys like createdAt create hot spots unless hashed.
13

Implement your own version of Promise.all.

Save to ▾
Show answer
It must keep result order and reject on the first failure. function promiseAll(items) { return new Promise((resolve, reject) => { const results = []; let done = 0; if (items.length === 0) return resolve(results); items.forEach((item, i) => { Promise.resolve(item).then((value) => { results[i] = value; done += 1; if (done === items.length) resolve(results); }, reject); }); }); } Promise.resolve handles plain values mixed with Promises.
14

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

Write a GitHub Actions workflow that tests and builds a MERN project on every pull request.

Save to ▾
Show answer
Run server and client jobs in parallel. name: ci on: pull_request: push: branches: [main] jobs: server: runs-on: ubuntu-latest defaults: run: working-directory: server steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm cache-dependency-path: server/package-lock.json - run: npm ci - run: npm run lint - run: npm test client: runs-on: ubuntu-latest defaults: run: working-directory: client steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: npm ci - run: npm run build
16

Write a docker-compose file to run a MERN app locally.

Save to ▾
Show answer
Three services on one network: services: mongo: image: mongo:8 volumes: - mongo-data:/data/db api: build: ./server environment: MONGO_URI: mongodb://mongo:27017/shop JWT_SECRET: dev-secret ports: - "5000:5000" depends_on: - mongo web: build: ./client ports: - "8080:80" depends_on: - api volumes: mongo-data: The API reaches the database through the service name mongo.
17

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

Tell me about a time you led a technical migration in a frontend codebase.

Save to ▾
Show answer
Situation: our React app still used Create React App and class components, and builds took several minutes. Task: as the senior developer I proposed moving to Vite and hooks. Action: I wrote a migration plan, moved the build tool first behind a feature branch, fixed environment variable names, then converted components gradually with tests. Result: dev server start went from 40 seconds to 2, builds became much faster, and the team adopted hooks patterns.
19

Explain indexes in MongoDB, compound indexes and the ESR rule.

Save to ▾
Show answer
An index is a sorted structure that lets queries avoid scanning every document. A compound index covers multiple fields in order, like { userId: 1, createdAt: -1 }, and supports queries on its leading prefix. The ESR guideline says order fields as Equality first, then Sort, then Range. Indexes speed reads but slow writes and use memory, so create them for real query patterns and drop unused ones.
20

Write an async helper that retries a failing call with exponential backoff.

Save to ▾
Show answer
Wait longer after each failure and give up after a limit. async function retry(fn, retries = 3, base = 500) { for (let attempt = 0; ; attempt++) { try { return await fn(); } catch (err) { if (attempt >= retries) throw err; const wait = base * 2 ** attempt + Math.random() * 100; await new Promise((r) => setTimeout(r, wait)); } } } The random jitter stops many clients from retrying at the same moment.
21

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

What is a custom hook and what rules must hooks follow?

Save to ▾
Show answer
A custom hook is a function whose name starts with use and that calls other hooks to package reusable stateful logic, such as useFetch, useDebounce or useAuth. Each component using it gets its own state. Hooks must be called at the top level of a component or another hook, never inside conditions, loops or nested functions, so React can match state by call order. The eslint-plugin-react-hooks rules enforce this.
23

Write a Redux Toolkit slice that fetches products with createAsyncThunk.

Save to ▾
Show answer
The slice handles pending, fulfilled and rejected states. export const fetchProducts = createAsyncThunk('products/fetch', async () => { const res = await fetch('/api/products'); return res.json(); }); const productsSlice = createSlice({ name: 'products', initialState: { items: [], status: 'idle', error: null }, reducers: {}, extraReducers: (builder) => { builder .addCase(fetchProducts.pending, (s) => { s.status = 'loading'; }) .addCase(fetchProducts.fulfilled, (s, a) => { s.status = 'done'; s.items = a.payload; }) .addCase(fetchProducts.rejected, (s, a) => { s.status = 'failed'; s.error = a.error.message; }); } }); export default productsSlice.reducer;
24

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

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

How do you handle pressure to ship quickly when you think a feature is not secure yet?

Save to ▾
Show answer
Situation: a product manager wanted a file-sharing feature live by Friday, but uploads had no type or access checks. Task: protect users without blocking the launch. Action: I explained the risk in business terms, proposed shipping with private pre-signed links and type limits first, and deferring sharing features to the next sprint. Result: we launched on time with the risky part removed, and the full feature followed a week later.
27

What is CSRF and how do SameSite cookies help prevent it?

Save to ▾
Show answer
Cross-site request forgery tricks a logged-in user's browser into sending a request to your site from a malicious page, and the browser attaches the user's cookies automatically. SameSite=Lax blocks cookies on cross-site POST requests and SameSite=Strict blocks them on all cross-site requests, which stops most CSRF. For extra safety use CSRF tokens on state-changing routes, never change state with GET, and verify the Origin header.
28

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

How would you use Redis caching in a MERN backend and what are the pitfalls?

Save to ▾
Show answer
Use the cache-aside pattern: check Redis for a key, return it on a hit, otherwise query MongoDB, store the result with a TTL and return it. Good targets are expensive, frequently read data like product listings. Pitfalls include stale data, so invalidate or update keys on writes; cache stampede when many requests miss at once, solved with locking or early refresh; and memory limits, managed with eviction policies.
30

Create a compound index for a user's order history query and verify it is used.

Save to ▾
Show answer
Query: orders of one user, newest first. db.orders.createIndex({ userId: 1, createdAt: -1 }) db.orders.find({ userId: ObjectId('...') }) .sort({ createdAt: -1 }) .limit(20) .explain('executionStats') Check that the winning plan shows IXSCAN, not COLLSCAN, and that totalDocsExamined is close to nReturned. In Mongoose, declare it with orderSchema.index({ userId: 1, createdAt: -1 }).
31

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

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

Compare worker_threads, cluster and child_process in Node.js.

Save to ▾
Show answer
cluster forks multiple Node processes that share one server port, used to use all CPU cores for an HTTP app, though PM2 or containers often do this instead. child_process runs a separate program or script, for example ffmpeg, communicating through stdio or IPC. worker_threads run JavaScript in parallel threads inside one process and can share memory through SharedArrayBuffer, best for CPU-heavy tasks like image processing or parsing.
34

What does React.memo do and when is it useful?

Save to ▾
Show answer
React.memo wraps a component so it skips re-rendering when its props are shallowly equal to the previous props. It helps with expensive components that re-render often because the parent re-renders, such as rows in a large table. It fails if you pass new object, array or function props every render, so pair it with useMemo and useCallback. Measure with React DevTools Profiler before adding it.
35

What are Mongoose middleware hooks and give a common use case.

Save to ▾
Show answer
Mongoose middleware are functions that run before or after operations such as save, validate, deleteOne, findOneAndUpdate and aggregate. A classic pre-save hook hashes a password only if it was modified: if (!this.isModified('password')) return; this.password = await bcrypt.hash(this.password, 12). Post hooks are used for logging or syncing. Note that query middleware like findOneAndUpdate does not trigger save hooks.
36

How do you plan a Node.js major version upgrade for a project your team maintains?

Save to ▾
Show answer
Situation: our services were on a Node version nearing end of life. Task: I owned the upgrade to the current LTS. Action: I read the changelog for breaking changes, upgraded locally, ran the test suite, fixed deprecated APIs and incompatible native packages, and rolled out to staging, then one production service, watching metrics. Result: all services moved within two sprints with no incidents, and I added an LTS calendar to our planning.
37

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

Write a production Dockerfile for a Node.js Express API.

Save to ▾
Show answer
Keep the image small and run as a non-root user. FROM node:22-alpine WORKDIR /app ENV NODE_ENV=production COPY package*.json ./ RUN npm ci --omit=dev COPY src ./src USER node EXPOSE 5000 CMD ["node", "src/server.js"] Copying package files first lets Docker cache the dependency layer, so code changes rebuild quickly. Add a .dockerignore with node_modules and .env.
39

What logging and monitoring should a production MERN app have?

Save to ▾
Show answer
Use structured JSON logs with a fast logger like pino, including a request id to trace a request across services, and ship logs to a central system. Track metrics such as request rate, error rate, latency percentiles, event-loop lag, memory and database performance, with dashboards and alerts. Add error tracking for both React and Node, uptime checks and distributed tracing with OpenTelemetry for microservices.
40

Compare session-based authentication with JWT-based authentication.

Save to ▾
Show answer
With sessions, the server stores session data in a store such as Redis and the browser holds only a session id cookie; revoking is easy, but every request needs a lookup. With JWTs, the token itself carries signed claims, so servers can verify it without a lookup, which suits microservices and mobile clients. JWTs are harder to revoke before expiry, so keep them short-lived and pair them with refresh tokens.

Frequently asked questions

What is asked in MERN Stack interviews for 7 years of experience?

With seven years, you are assessed as a lead or staff engineer. Expect deep system design across services, data modelling for high scale, multi-region availability, cost optimisation and choosing between monolith and microservices. Interviewers want evidence of technical vision, setting engineering standards, improving delivery processes and working with product and business stakeholders on roadmaps and trade-offs.

How many questions should I practise before a MERN Stack interview?

Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.

Which topics matter most for 7 years of experience?

Focus first on Distributed system design, Engineering standards, Technical roadmap, Stakeholder management.