MERN Interview Prep Guide
MERN Interview Prep Guide
js
MERN STACK
Interview Preparation Guide
Real interview questions with detailed answers, code samples & references
Table of Contents
■ SECTION 1: BEGINNER
■ 1.1 What is MERN? — Overview & Architecture
■ 1.2 MongoDB Basics — Documents, Collections, CRUD
■ 1.3 [Link] Basics — Routing, Middleware, REST
■ 1.4 React Basics — Components, JSX, Props, State
■ 1.5 [Link] Basics — Event Loop, Modules, npm
■ 1.6 Beginner Interview Questions (15 Q&As;)
■ SECTION 2: INTERMEDIATE
■ 2.1 MongoDB — Aggregation Pipeline, Indexes, Schema Design
■ 2.2 [Link] — Authentication, Error Handling, Middleware Chains
■ 2.3 React — Hooks Deep Dive, Context API, Performance
■ 2.4 [Link] — Streams, Async/Await, Event Emitter
■ 2.5 REST API Design & HTTP Deep Dive
■ 2.6 Intermediate Interview Questions (15 Q&As;)
■ SECTION 3: ADVANCED
■ 3.1 MongoDB — Transactions, Replication, Sharding
■ 3.2 [Link] — Cluster, Worker Threads, Performance Tuning
■ 3.3 React — Advanced Patterns, Suspense, SSR, Testing
■ 3.4 System Design — Scalability, Caching, Microservices
■ 3.5 Security — JWT Deep Dive, OWASP Top 10 in MERN
■ 3.6 Advanced Interview Questions (15 Q&As;)
SECTION 1: BEGINNER
MERN is a full-stack JavaScript framework using four technologies to build modern web
applications entirely in JavaScript — from the database to the browser.
Database MongoDB NoSQL document store; stores JSON-like BSON data 27017
Backend API [Link] Minimal web framework on top of [Link]; handles HTTP
5000
routes
/ 3001
Runtime [Link] JavaScript runtime; runs server-side code outside the browser
—
■ Note: All four layers use JavaScript/JSON, making it easy to share code and data models
across the full stack.
Document
A JSON-like object with key-value pairs. Maximum size 16MB. Nested documents and arrays
allowed.
Collection
A group of documents. Schema-less — documents in the same collection can have different fields.
_id field
Every document has a unique _id (ObjectId by default). Acts as primary key.
ObjectId
12-byte BSON type: 4B timestamp + 5B random + 3B counter. Globally unique, sortable by creation
time.
// CREATE
const user = await [Link]({ name: 'Ali', email: 'ali@[Link]', age:
25 });
// READ
const users = await [Link]({ age: { $gte: 18 } });
const single = await [Link]('64abc123...');
// UPDATE
await [Link](id, { $set: { age: 26 } }, { new: true });
// DELETE
await [Link](id);
[Link] is a minimal, unopinionated web framework for [Link]. It provides routing, middleware
support, and HTTP utility methods for building APIs and web apps.
JS
const express = require('express');
const app = express();
■ Tip: Always use async/await with try/catch in route handlers, or pass errors to next(err) for
centralized error handling.
React is a JavaScript library (not a framework) for building user interfaces through a
component-based model. React uses a Virtual DOM to efficiently update only the parts of the real
DOM that changed.
Concept Description
Props Read-only data passed from parent to child. Components receive props as function arguments.
Concept Description
State Mutable data managed inside a component. useState() hook triggers re-render on change.
Virtual DOM In-memory representation of the DOM. React diffs it against the real DOM (reconciliation) to min
One-way data flow Data flows down from parent to child via props. Events bubble up via callback props.
JSX
// Functional Component with State
import { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => [Link]())
.then(data => { setUsers(data); setLoading(false); });
}, []); // [] = run only on mount
return (
<ul>
{[Link](u => <li key={u._id}>{[Link]}</li>)}
</ul>
);
}
■ Note: [Link] is single-threaded but handles concurrency through the Event Loop + libuv's
thread pool for I/O operations.
Concept Description
Event Loop Executes callbacks from the event queue when the call stack is empty. Phases: timers, pending
CommonJS Modules require() / [Link]. Built-in module system for [Link] (pre-ESM).
ES Modules (ESM) import / export. Modern JS modules; supported in [Link] 12+ with .mjs or "type":"module" in pa
Concept Description
npm / [Link] Node Package Manager. [Link] stores dependencies, scripts, and metadata.
process object Global object providing env vars ([Link]), arguments, exit codes, and stdin/stdout streams
Q What is the difference between SQL and NoSQL databases? Why does MERN
1 use MongoDB?
■ Answer:
SQL databases (MySQL, PostgreSQL) use structured tables with a fixed schema and relations
enforced by foreign keys. They are ACID-compliant and excellent for structured, relational data.
NoSQL databases (MongoDB) store data as flexible JSON-like documents — no fixed schema.
Ideal for hierarchical, rapidly-changing data structures and horizontal scaling.
MERN uses MongoDB because its JSON documents match JavaScript objects natively,
eliminating object-relational mapping overhead and keeping the entire stack in one language
ecosystem.
■ Ref: MongoDB Docs — SQL to MongoDB Mapping; CAP Theorem (Brewer 2000)
■ Answer:
Middleware is a function that has access to req, res, and next. It sits in the request-response
pipeline and can execute code, modify req/res objects, end the cycle, or call next() to pass control
to the next middleware. Examples: [Link]() (body parsing), cors(), authentication guards,
logging (Morgan).
JS
// Custom logging middleware
function logger(req, res, next) {
[Link](`${[Link]} ${[Link]} - ${[Link]()}`);
next(); // MUST call next() or the request hangs
}
[Link](logger); // applies to all routes
Q What is the Virtual DOM in React and why is it faster than the real DOM?
3
■ Answer:
The Virtual DOM (VDOM) is a lightweight JavaScript object representation of the real DOM tree.
When state/props change, React creates a new VDOM, diffs it against the previous VDOM
(reconciliation using the Fiber algorithm), and calculates the minimum set of real DOM changes
needed.
The real DOM is slow to update because changes trigger reflow/repaint in the browser. By
batching and minimizing real DOM operations, React achieves better performance — especially in
apps with frequent UI updates.
■ Ref: React Docs — Reconciliation; Lin Clark — A Cartoon Intro to Fiber (React Conf 2017)
■ Answer:
Props (properties) are read-only data passed from a parent component to a child. A child cannot
modify its props. They enable component reusability and one-directional data flow.
State is mutable data owned and managed by the component itself. When state changes (via
setState or the useState setter), React schedules a re-render of that component and its children.
Rule of thumb: if data comes from outside — it's a prop. If the component manages it — it's state.
JS
// Props: passed from parent
function Greeting({ name }) { // name is a prop
return <h1>Hello, {name}</h1>;
}
Q What is the event loop in [Link] and how does it handle async operations?
5
■ Answer:
[Link] runs JavaScript in a single thread. The event loop is the mechanism that allows it to
handle thousands of concurrent operations without creating new threads for each.
When an async operation (file I/O, network request, timer) is initiated, [Link] offloads it to libuv's
thread pool or the OS. When it completes, a callback is queued in the event queue. The event loop
picks up callbacks when the call stack is empty and executes them.
Phases in order: timers (setTimeout/setInterval) → pending I/O callbacks → idle → poll (wait for
I/O) → check (setImmediate) → close callbacks.
JS
[Link]("1 - sync");
setTimeout(() => [Link]('3 - timer (macrotask)'), 0);
[Link]().then(() => [Link]('2 - microtask'));
[Link]("4 - sync end");
// Output: 1, 4, 2, 3
// Microtasks (Promises) run BEFORE macrotasks (setTimeout)
■ Ref: [Link] Docs — Event Loop; Philip Roberts — What the heck is the event loop (JSConf EU 2014)
■ Answer:
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks HTTP
requests made from a different origin (different protocol, domain, or port) than the server. Since
React (localhost:3000) and Express (localhost:5000) run on different ports in development, CORS
must be explicitly enabled on the server.
The cors npm package adds the necessary Access-Control-Allow-Origin headers to Express
responses.
JS
const cors = require('cors');
■ Ref: MDN — CORS; npm cors package docs; RFC 6454 (Web Origin Concept)
■ Answer:
== (loose equality) performs type coercion before comparison — it converts values to a common
type. === (strict equality) compares both value AND type with no coercion. Always use === in
JavaScript to avoid unexpected bugs from implicit type conversions.
JS
0 == false // true (coercion: false → 0)
0 === false // false (different types)
'1' == 1 // true (coercion: string → number)
'1' === 1 // false
null == undefined // true (special case)
null === undefined // false
■ Answer:
A Promise is an object representing the eventual completion (or failure) of an async operation.
async/await is syntactic sugar over Promises that makes asynchronous code look synchronous
and easier to read.
An async function always returns a Promise. await pauses execution inside the async function
until the Promise resolves (or throws if rejected). Error handling uses try/catch instead of .catch().
JS
// Promise chain
fetch('/api/users')
.then(res => [Link]())
.then(data => [Link](data))
.catch(err => [Link](err));
■ Answer:
useEffect is a React Hook that performs side effects in function components. Side effects include:
fetching data from APIs, setting up subscriptions, manually manipulating the DOM, starting timers.
The dependency array controls when the effect runs: empty [] = once on mount only; [value] =
re-runs when value changes; no array = runs after every render.
JS
useEffect(() => {
// Setup: fetch data when userId changes
fetch(`/api/users/${userId}`)
.then(r => [Link]()).then(setUser);
Q
1 What are the HTTP methods used in a REST API and what do they represent?
0
■ Answer:
REST APIs use standard HTTP methods to represent CRUD operations on resources:
GET — Read (retrieve a resource, no body, idempotent)
POST — Create (submit data, non-idempotent, body required)
PUT — Replace (full update of resource, idempotent)
PATCH — Partial update (only changed fields, idempotent)
DELETE — Remove a resource (idempotent)
Idempotent means calling the same request multiple times produces the same result.
JS
GET /api/users → get all users
GET /api/users/:id → get one user
POST /api/users → create user
PUT /api/users/:id → replace user fully
PATCH /api/users/:id → partial update
DELETE /api/users/:id → delete user
■ Ref: RFC 7231 — HTTP/1.1 Semantics; Roy Fielding — REST Dissertation (2000)
SECTION 2: INTERMEDIATE
The aggregation pipeline transforms documents through a sequence of stages. Each stage
outputs documents that become input to the next stage. Far more powerful and efficient than
multiple find() calls.
JS
// Aggregation: total revenue per category, only categories with > $1000
[Link]([
{ $match: { status: 'completed' } },
{ $unwind: "$items" },
{ $group: {
_id: "$[Link]",
totalRevenue: { $sum: "$[Link]" },
orderCount: { $count: {} }
}},
MongoDB Indexes
Index Type Use Case Example
■ Tip: ESR Rule for compound indexes: Equality fields first, Sort fields second, Range fields last.
JS
// JWT Auth Middleware
const jwt = require('jsonwebtoken');
useState Local component state Returns [state, setter]; setter is async (batched)
useEffect Side effects; runs after render Cleanup via return fn; deps array controls frequency
useContext Consume a React Context value Avoids prop drilling; re-renders on context change
useMemo Memoize expensive computed valueOnly recomputes when deps change; avoids re-computation
useCallback Memoize a function reference Prevents child re-renders when passing callbacks as props
useReducer Complex state logic (Redux-like) dispatch(action) → reducer(state, action) → new state
Custom Hook Reusable stateful logic (useXxx) Prefixed with "use"; can use other hooks inside
JSX
// Custom Hook: useLocalStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = [Link](key);
return item ? [Link](item) : initialValue;
} catch { return initialValue; }
});
// Usage
JS
// [Link] — parallel requests
async function getDashboard(userId) {
const [user, orders, notifications] = await [Link]([
[Link](userId),
[Link]({ userId }),
[Link]({ userId, read: false })
]);
return { user, orders, notifications };
}
■ Note: [Link] rejects immediately if any promise rejects. Use [Link]() to get
results of all, including failures.
Principle Description
Stateless Each request contains all information needed. Server stores no client session state.
HTTP Status Codes 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500
HATEOAS Hypermedia links in responses guide client navigation (optional but RESTful)
■ Answer:
The aggregation pipeline is a sequence of stages that process and transform documents. find()
retrieves documents with simple matching and projection but cannot compute aggregates, join
collections, reshape output, or perform multi-stage transformations.
The pipeline is processed server-side, reducing data sent to the application. Stages like $match
should appear early to reduce the working set. $group computes accumulators. $lookup joins
collections. This enables analytics impossible with find() alone.
JS
// Average order value per customer
[Link]([
{ $match: { createdAt: { $gte: new Date('2024-01-01') } } },
{ $group: { _id: "$customerId",
avgValue: { $avg: "$total" },
count: { $sum: 1 } } },
{ $sort: { avgValue: -1 } },
{ $limit: 10 }
]);
■ Answer:
JWT (JSON Web Token) is a compact, URL-safe token format. A JWT consists of three
Base64URL-encoded parts separated by dots: Header (algorithm) . Payload (claims/data) .
Signature.
Flow: user logs in → server verifies credentials → server signs a JWT with a secret → client stores
the JWT → client sends JWT in Authorization: Bearer header → server verifies signature on each
request.
Security best practices: short expiry (15min) with refresh tokens, store access token in memory
(not localStorage), use httpOnly cookies for refresh tokens, always use HTTPS, rotate secrets
regularly.
JS
const jwt = require('jsonwebtoken');
■ Ref: RFC 7519 — JSON Web Token; OWASP — JWT Security Cheat Sheet
■ Answer:
Both are memoization hooks that prevent unnecessary recomputation between renders.
useMemo memoizes the RESULT of a function — it caches a computed value and only
recomputes it when its dependencies change. Use for expensive calculations (sorting large arrays,
complex filtering).
useCallback memoizes the FUNCTION REFERENCE itself. Use when passing callbacks to child
components that use [Link], to prevent unnecessary re-renders because a new function
reference is created on every parent render.
JS
// useMemo: memoize expensive computed value
const sortedList = useMemo(() =>
[...items].sort((a, b) => [Link] - [Link]),
[items] // only re-sort when items changes
);
■ Ref: React Docs — useMemo; useCallback; When to useMemo and useCallback — Kent C. Dodds
■ Answer:
[Link] is single-threaded for JavaScript execution but uses libuv's I/O engine (which has a thread
pool) for file system, DNS, crypto, and other async I/O operations.
When async I/O is initiated, [Link] registers a callback and continues executing other code.
When the I/O completes, libuv signals the event loop which queues the callback.
This non-blocking model allows one [Link] process to handle thousands of concurrent
connections because it never waits for I/O — it processes other requests while I/O is in progress.
CPU-intensive tasks (image processing, heavy computation) block the event loop and require
worker threads or child processes.
■ Ref: [Link] Docs — About Node; libuv Documentation; Bert Belder — Everything you need to know about
[Link]
Q What is the Context API in React and when would you use it over Redux?
5
■ Answer:
React Context provides a way to share values between components without passing props
through every level of the tree (prop drilling). A Context consists of a Provider (supplies the value)
and Consumer or useContext hook (reads the value).
Use Context for: theme, locale, authenticated user, simple shared state.
Use Redux/Zustand when: state is complex with many actions and reducers, you need
middleware (logging, async actions), time-travel debugging (Redux DevTools), or the app is large
and many unrelated components need the same state.
JS
// Create and provide context
const AuthContext = createContext(null);
■ Answer:
Embed (denormalize): Store related data as a nested document or array within the parent
document.
Best when: data is frequently accessed together, the nested data is "owned by" the parent, data
won't grow unboundedly.
Reference (normalize): Store a reference (_id) to another document in a separate collection.
Best when: the referenced data is large or shared across many documents, many-to-many
relationships, the referenced data changes frequently, or the document would exceed the 16MB
limit.
Rule of thumb: embed for "has-a" one-to-few; reference for one-to-many and many-to-many.
JS
// EMBED: blog post with its comments (few comments)
{ _id: ObjectId, title: "Post", comments: [
{ author: "Ali", text: "Great post!" },
{ author: "Sara", text: "Thanks!" }
]}
■ Ref: MongoDB Docs — Data Modeling Introduction; MongoDB Schema Design Patterns (6 Patterns)
■ Answer:
All three schedule callbacks asynchronously, but in different phases of the event loop:
[Link]() runs after the current operation completes but BEFORE the event loop
continues — even before I/O callbacks and timers. It runs in the "microtask queue" alongside
Promises.
[Link]() also runs as a microtask — after nextTick.
setImmediate() runs in the check phase of the event loop — after I/O callbacks.
setTimeout(fn, 0) runs in the timers phase — after I/O, may run before or after setImmediate
depending on the context.
JS
setTimeout(() => [Link]('4 - setTimeout'), 0);
setImmediate(() => [Link]('3 - setImmediate'));
[Link]().then(() => [Link]('2 - Promise'));
[Link](() => [Link]('1 - nextTick'));
// Output order: nextTick → Promise → setImmediate → setTimeout
// (setTimeout vs setImmediate order can vary outside I/O context)
Q What are React keys and why are they important in lists?
8
■ Answer:
Keys are special props that help React's reconciliation algorithm identify which list items have
changed, been added, or removed. Without keys, React re-renders the entire list on any change.
Keys must be unique among siblings and stable (not based on array index if items can be
reordered/filtered). Using array index as key causes bugs: when items are reordered, React
associates state with the wrong component because the key (index) is the same but the item
changed.
JS
// BAD: using index as key
[Link]((item, i) => <Item key={i} {...item} />)
SECTION 3: ADVANCED
For production MERN apps, understanding MongoDB's high-availability and scalability features is
essential for senior and lead-level interviews.
JS
// Multi-document ACID Transaction (requires Replica Set or Sharded Cluster)
const session = await [Link]();
[Link]();
try {
await [Link](
senderId, { $inc: { balance: -amount } }, { session }
);
await [Link](
receiverId, { $inc: { balance: +amount } }, { session }
);
await [Link]([{ senderId, receiverId, amount }], { session });
await [Link]();
} catch (err) {
await [Link]();
throw err;
} finally {
[Link]();
}
Feature Description
Replica Set 3+ nodes (primary + secondaries). Automatic failover if primary goes down. Read preference can rou
Write Concern w:1 = primary confirms; w:"majority" = majority of nodes confirm (durability guarantee). j:true = wait fo
Read Concern local (default), majority (only committed data), linearizable (most recent committed data).
Sharding Horizontal partitioning across multiple servers via a shard key. Enables petabyte-scale storage and th
Feature Description
Change Streams Real-time notifications of data changes using MongoDB's oplog. Used for event-driven architectures.
Atlas Search Full-text search powered by Apache Lucene, integrated into MongoDB Atlas.
JS
// Cluster module: utilize all CPU cores
const cluster = require('cluster');
const os = require('os');
if ([Link]) {
const numCPUs = [Link]().length;
for (let i = 0; i < numCPUs; i++) [Link]();
[Link]('exit', (worker) => {
[Link](`Worker ${[Link]} died — restarting`);
[Link]();
});
} else {
// Each worker runs the Express app
require('./app');
}
if (isMainThread) {
const worker = new Worker('./[Link]');
[Link]('message', result => [Link]('Result:', result));
[Link]({ data: [1, 2, 3, 4, 5] });
} else {
[Link]('message', ({ data }) => {
const result = [Link]((a, b) => a + b, 0); // heavy CPU work
[Link](result);
});
}
Server Components (RSC) Components that render only on the server; [Link]
client JS
13+bundle
App Router
impactdefault components
Compound Components Components share implicit state via Context Tabs, Accordions, Select menus with flexible comp
JSX
// Compound Component Pattern — Tabs
const TabsContext = createContext(null);
Injection (NoSQL injection) Never interpolate user input into queries. Use Mongoose which parameterizes. Sanitize with
Broken Authentication Short-lived JWTs, httpOnly refresh tokens, bcrypt for password hashing (saltRounds 12+), a
Sensitive Data Exposure HTTPS everywhere, encrypt sensitive fields at rest, never log passwords/tokens, use enviro
XSS (Cross-Site Scripting) React escapes JSX by default. Avoid dangerouslySetInnerHTML. Set Content-Security-Polic
CSRF Use SameSite=Strict cookies. CSRF tokens for form submissions. Verify Origin header on s
Broken Access Control Check authorization on every route (not just auth). Field-level permissions. Resource owners
Security Misconfiguration [Link] for security headers, disable X-Powered-By, use .env files, rotate secrets, review C
Mass Assignment Whitelist allowed fields in Mongoose: never [Link]([Link]) directly. Use pick() to extr
JS
// Security middleware stack (install: npm i helmet morgan
express-rate-limit)
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
max: 100,
message: { error: 'Too many requests, please try again later.' }
});
[Link]('/api/', limiter);
Q How would you design a scalable real-time notification system in a MERN app?
1
■ Answer:
For real-time delivery, use either WebSockets ([Link]) or Server-Sent Events (SSE).
Architecture: Client connects to [Link] via [Link] → events published to Redis Pub/Sub → all
[Link] instances subscribed to Redis receive the event → emit to the specific connected client.
This works across a cluster because even if the client is connected to Node instance #3, any
instance can publish to Redis and instance #3 will receive it and forward to the client.
Store notification records in MongoDB (for persistence/history). Mark as read via REST API.
JS
// [Link] + Redis adapter for cluster support
const { createAdapter } = require('@[Link]/redis-adapter');
const { createClient } = require('redis');
■ Ref: [Link] Docs — Redis Adapter; Redis Pub/Sub; Designing Real-Time Systems — LeadDev
Q What is React Server Components (RSC) and how does it differ from SSR?
2
■ Answer:
Server-Side Rendering (SSR) renders components on the server to HTML for the initial page load,
then hydrates the full component tree on the client (client JS bundle includes all components).
React Server Components (RSC) render exclusively on the server and are never hydrated — their
JavaScript is never shipped to the client, reducing bundle size. RSC can directly access
databases and file systems.
Client Components (with "use client" directive) are still hydrated normally.
In [Link] App Router, all components are Server Components by default. This enables data
fetching inside components without API routes, with zero client-side JavaScript cost for server-only
components.
JS
// Server Component — runs on server only, no "use client"
// Can await directly, access DB, no useState/useEffect
async function UserProfile({ userId }) {
const user = await [Link](userId); // direct DB call
return <div>{[Link]}</div>;
}
■ Ref: React Docs — Server Components; [Link] App Router Docs; Dan Abramov — RSC From Scratch
Q Explain the memory leak scenarios in [Link] and how you would debug them.
3
■ Answer:
Common [Link] memory leak sources:
1. Global variables accumulating data over time (never garbage collected)
2. Event listeners not removed after use (EventEmitter with no removeListener)
3. Closures holding references to large objects
4. Timers (setInterval) not cleared
5. Mongoose connections not pooled properly
6. Caches growing unboundedly (no TTL or LRU eviction)
Debugging: [Link] --inspect + Chrome DevTools heap snapshots, [Link]()
monitoring, [Link] (clinic doctor / clinic heapprofiler), node --heap-prof.
JS
// BAD: event listener leak — added repeatedly
function onRequest(req, res) {
[Link]('data', handler); // added on every request!
}
// Monitor memory
setInterval(() => {
const { heapUsed } = [Link]();
[Link](`Heap: ${[Link](heapUsed/1024/1024)}MB`);
}, 30000);
■ Ref: [Link] Docs — Diagnostics; [Link] by NearForm; [Link] Memory Leak Patterns — Nodesource
■ Answer:
Optimistic updates immediately apply the expected change to the UI before the server confirms,
making the app feel instant. If the server request fails, roll back to the previous state.
Pattern: (1) Save the current state as a backup. (2) Optimistically update state. (3) Await the API
call. (4) On success — done. (5) On failure — restore the backup state and show an error.
React Query and SWR have built-in optimistic update support with automatic rollback.
JS
// Optimistic update for a "like" button
async function handleLike(postId) {
const prevPosts = posts; // backup
// Optimistically update UI immediately
setPosts(prev => [Link](p =>
p._id === postId ? { ...p, likes: [Link] + 1 } : p
));
try {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
} catch (err) {
setPosts(prevPosts); // rollback on failure
[Link]('Failed to like post');
}
}
■ Ref: TanStack Query Docs — Optimistic Updates; SWR Docs — Mutation; React 19 useOptimistic hook
Q What is the N+1 problem in MongoDB with Mongoose? How do you solve it?
5
■ Answer:
The N+1 problem: fetching N parent documents and then making 1 additional query per parent to
fetch related data — resulting in N+1 total queries.
In Mongoose: looping over posts and calling .find({ postId }) inside the loop is N+1.
Solutions:
1. Mongoose .populate() — joins the collection reference in one additional batched query
2. MongoDB $lookup aggregation — single pipeline, no round trips
3. Dataloader pattern — batch and cache database calls (Facebook's DataLoader library)
JS
// BAD: N+1 — 1 query for posts + N queries for authors
const posts = await [Link]();
for (const post of posts) {
[Link] = await [Link]([Link]); // N queries!
}
■ Answer:
Creating a new database connection for every HTTP request is expensive (TCP handshake, auth,
memory allocation). Connection pooling maintains a pool of pre-established connections that are
reused across requests, dramatically reducing latency and resource usage.
Mongoose maintains a single connection per model by default (via [Link]()). The pool
size (default 5) is configurable. For high-traffic apps, tune maxPoolSize and monitor with
MongoDB Atlas metrics or Mongoose connection events.
Anti-pattern: calling [Link]() inside route handlers or creating new connections per
request.
JS
// Connect ONCE at app startup — Mongoose manages the pool
[Link]([Link].MONGO_URI, {
maxPoolSize: 20, // max concurrent connections
minPoolSize: 5, // keep minimum alive
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});
// Graceful shutdown
[Link]('SIGTERM', async () => {
await [Link]();
[Link](0);
});
■ Ref: Mongoose Docs — Connections; MongoDB Connection Pooling Best Practices; 12-Factor App — Config
■ Answer:
RBAC restricts system access based on assigned roles. Design:
1. Data model: User has a role field ("admin", "editor", "viewer")
2. JWT: Include the role in the token payload when signing
3. Middleware: After authentication, check if the user's role has permission for the requested
resource/action
4. For fine-grained control, use permission arrays instead of just roles
5. Never trust client-sent role claims — always read from the signed JWT or re-fetch from DB for
sensitive operations
JS
// Role-based middleware factory
function authorize(...allowedRoles) {
return (req, res, next) => {
if () {
return [Link](403).json({ message: "Forbidden" });
}
next();
};
}
// Apply to routes
[Link]('/api/users/:id',
authenticate,
authorize('admin'), // only admins can delete
deleteUser
);
[Link]('/api/reports',
authenticate,
authorize('admin', 'editor'),
getReports
);
■ Ref: OWASP — Access Control Cheat Sheet; NIST RBAC Standard (FIPS 140)
jest / supertest Unit testing + HTTP API integration testing npm i -D jest supertest
react-query / @tanstack/query
Server state management, caching, async data npm i @tanstack/react-query
axios HTTP client with interceptors, better than fetch npm i axios
useRef const ref = useRef(init) DOM refs, mutable values without re-render
useReducer const [s, d] = useReducer(reducer, init) Complex state with multiple sub-values