MERN_Stack_Interview_Guide
MERN_Stack_Interview_Guide
Plus JavaScript, HTML/CSS, SQL, Git, REST APIs, JWT, Deployment, Coding Rounds, Project & HR Questions
Table of Contents
Answer: Semantic HTML uses tags that describe their meaning/content ( <header> , <nav> , <article> , <section> , <footer> ) instead
of generic tags like <div> for everything. It improves accessibility (screen readers understand structure), SEO (search engines weigh
semantic content), and code readability/maintainability.
<header>...</header>
<nav>...</nav>
<main>
<article>
<section>...</section>
</article>
</main>
<footer>...</footer>
Follow-up: Difference between <div> and <span> ? (block vs inline, structural vs text-level)
Tip: Mention accessibility (ARIA roles) — interviewers like candidates who think beyond visuals.
Answer: Every element is a rectangular box made of, from inside out: content → padding → border → margin. box-sizing: content-
box (default) excludes padding/border from the declared width; box-sizing: border-box includes them, which is why most projects set *
{ box-sizing: border-box; } globally.
Tip: Draw the box model mentally and explain width = content + padding + border when asked to debug layout issues.
Answer: - relative — positioned relative to its own normal position; still occupies original space. - absolute — removed from normal
flow, positioned relative to nearest positioned (non-static) ancestor. - fixed — positioned relative to the viewport; stays put on scroll. -
sticky — hybrid: behaves as relative until a scroll threshold, then sticks like fixed within its parent.
Follow-up: What happens if no ancestor has position set for an absolute child? (falls back to the initial containing block, i.e., <html> )
Answer: Flexbox is one-dimensional (row OR column) — ideal for navbars, button groups, centering content. Grid is two-dimensional
(rows AND columns) — ideal for full page layouts, dashboards, card grids with precise placement.
Tip: Say “Flexbox for components, Grid for page layout” — a common, well-liked rule of thumb.
Answer: Specificity decides which rule wins when multiple rules target the same element. Order of weight (highest to lowest): inline styles
> IDs > classes/attributes/pseudo-classes > elements/pseudo-elements. !important overrides all specificity (use sparingly).
Follow-up: How would you fix a specificity war without using !important ? (Increase specificity naturally, or restructure CSS with
methodologies like BEM.)
Answer: Media queries apply CSS conditionally based on device characteristics (width, orientation, resolution). Mobile-first design starts
with base styles for small screens and adds complexity via min-width breakpoints.
/* Mobile first */
.card { width: 100%; }
@media (min-width: 768px) { .card { width: 50%; } }
@media (min-width: 1024px) { .card { width: 33%; } }
Tip: Mention rem / em over fixed px for scalable typography, and CSS clamp() for fluid sizing.
Q7. Difference between localStorage , sessionStorage , and cookies.
Answer: | Feature | localStorage | sessionStorage | Cookies | |—|—|—|—| | Persistence | Until manually cleared | Until tab closes |
Configurable expiry | | Size | ~5-10MB | ~5-10MB | ~4KB | | Sent to server | No | No | Yes, with every request | | Use case | Long-term client
data | Per-tab temp data | Auth tokens, server-read data |
Follow-up: Where should you NOT store a JWT and why? (Avoid localStorage for highly sensitive tokens due to XSS risk; httpOnly cookies
are safer.)
Answer: z-index controls the stack order of positioned elements along the z-axis. It only works on elements with a position other than
static . A stacking context is formed by certain properties ( position + z-index , opacity < 1 , transform , etc.) — z-index values
are only compared within the same stacking context.
Tip: A classic bug: a high z-index child doesn’t show above a sibling because its parent created a new stacking context with a lower z-
index — always check ancestor stacking contexts when debugging.
Chapter 2: JavaScript — Core Concepts
Q1. var vs let vs const — explain scope and hoisting differences.
Answer: - var — function-scoped, hoisted and initialized as undefined , can be redeclared. - let — block-scoped, hoisted but NOT
initialized (Temporal Dead Zone), cannot be redeclared in the same scope. - const — block-scoped like let , must be initialized at
declaration, binding cannot be reassigned (but object/array contents CAN be mutated).
Follow-up: Why is const not the same as “immutable”? (It freezes the variable binding, not the value — use [Link]() for true
immutability.)
Tip: Always default to const , use let when reassignment is needed, avoid var in modern code.
Answer: A closure is a function that “remembers” variables from its lexical scope even after the outer function has returned. Closures
power patterns like data privacy, currying, and memoization.
function counter() {
let count = 0;
return function () {
count++;
return count;
};
}
const increment = counter();
increment(); // 1
increment(); // 2 — count persists between calls
Follow-up: How would you use closures to create a private counter/module pattern? (As above — count is inaccessible from outside
except via the returned function.)
Tip: Closures are asked constantly — practice explaining the “memory” analogy clearly and be ready to write one live.
Answer: this is determined by how a function is called, not where it’s defined (except arrow functions): - Regular function call →
this is undefined (strict mode) or global object. - Method call ( [Link]() ) → this is obj . - Arrow functions → this is lexically
inherited from the enclosing scope (no own this ). - call / apply / bind → explicitly set this . - Constructor ( new Fn() ) → this is the
newly created object.
const obj = {
name: "Claude",
regular() { return [Link]; }, // "Claude"
arrow: () => this?.name, // undefined — inherits outer `this`
};
Follow-up: Why do arrow functions in class methods avoid the “this is undefined” bug in event handlers? (They capture this from the
surrounding class context at definition time.)
Q4. Explain the Event Loop, Call Stack, and Task Queues (Microtasks vs Macrotasks).
Answer: JavaScript is single-threaded. The Call Stack executes synchronous code. Async operations (timers, I/O) are handed to Web
APIs/Node APIs, and their callbacks go into queues: - Microtask queue — Promises, queueMicrotask , MutationObserver (higher
priority). - Macrotask queue — setTimeout , setInterval , I/O callbacks.
The Event Loop checks: is the call stack empty? → drain ALL microtasks → run one macrotask → repeat.
[Link]("1");
setTimeout(() => [Link]("2"), 0);
[Link]().then(() => [Link]("3"));
[Link]("4");
// Output: 1, 4, 3, 2
Follow-up: Why does a [Link] run before a setTimeout(fn, 0) ? (Microtask queue is fully drained before the next macrotask.)
Tip: This is one of the top 3 most-asked JS questions in MERN interviews — practice tracing 2-3 tricky examples out loud.
Answer: A Promise represents an eventual value from an async operation, with states: pending , fulfilled , rejected . async/await is
syntactic sugar over Promises, making async code read like synchronous code.
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("data"), 1000);
});
}
Follow-up: How do you run multiple promises in parallel? ( [Link] , [Link] , [Link] , [Link] — know the
difference between each.)
Tip: Be ready to explain [Link] fails fast on first rejection, while allSettled waits for all and reports each outcome.
Answer: Every JS object has an internal link [[Prototype]] (accessible via __proto__ or [Link] ) to another object.
Property lookups traverse this chain until found or null is reached. class syntax is syntactic sugar over this same mechanism.
Answer: Functions that take other functions as arguments or return functions. Array methods map , filter , reduce are common HOFs.
Answer: == compares values after type coercion (converts operands to a common type); === (strict equality) compares both value AND
type without coercion. Always prefer === to avoid unexpected bugs.
Q9. Explain debounce and throttle. When would you use each?
Answer: Both limit how often a function executes on rapid events (scroll, resize, keystroke): - Debounce — waits until the event stops
firing for X ms before running (e.g., search-as-you-type: wait until user pauses typing). - Throttle — runs at most once every X ms
regardless of event frequency (e.g., scroll-position tracking).
Answer: Destructuring extracts values from arrays/objects into variables. Spread ( ... ) expands iterables; rest ( ... ) collects remaining
items into an array — same syntax, opposite direction.
Q11. Explain map , filter , reduce differences with a real use case.
Answer: map transforms every element 1:1 (same length output). filter selects elements matching a condition (shorter or equal
output). reduce accumulates all elements into a single value (number, object, array — anything).
Tip: Interviewers love asking you to compute totals/group data with reduce live — practice a grouping example ( reduce into an object).
Answer: Currying transforms a function taking multiple arguments into a sequence of functions each taking a single argument, enabling
partial application.
Follow-up: Real-world use case? (Configurable, reusable functions — e.g., pre-configured API request builders, logging with fixed
prefixes.)
Answer: A shallow copy duplicates only the top level; nested objects/arrays still reference the same memory. A deep copy duplicates
every nested level, fully independent.
Tip: Mention structuredClone() — the modern native way to deep clone, better than [Link]([Link]()) since it handles
Dates, Maps, Sets, etc.
let a; // undefined
let b = null; // null, deliberately empty
typeof undefined; // "undefined"
typeof null; // "object" (a famous historical JS bug)
Answer: ES Modules ( import / export ) are the standard browser/modern-Node module system, statically analyzed, tree-shakeable.
CommonJS ( require / [Link] ) is Node’s traditional system, resolved at runtime, synchronous.
// ES Module
export const add = (a, b) => a + b;
import { add } from "./[Link]";
// CommonJS
[Link] = { add };
const { add } = require("./math");
Follow-up: Can you mix CommonJS and ESM in one Node project? (Yes, with care — .mjs / .cjs extensions or "type": "module" in
[Link], and dynamic import() to load ESM from CommonJS.)
Chapter 3: JavaScript — Output-Based Questions
These “guess the output” questions are extremely common in MERN screening rounds because they test real understanding of hoisting,
closures, the event loop, and coercion — not memorization.
Q1.
Output: 3 3 3 Why: var is function-scoped — all three callbacks share the same i , which is 3 by the time the timers fire.
Fix: Use let (block-scoped, new binding per iteration) → outputs 0 1 2 .
Q2.
[Link](typeof NaN);
[Link](NaN === NaN);
Output: "number" then false Why: NaN is technically of type number ; by IEEE 754 spec, NaN never equals itself.
Q3.
function foo() {
[Link](this);
}
const obj = { foo };
[Link]();
const unbound = [Link];
unbound();
Output: logs obj , then logs undefined (strict mode) or the global object ( window / globalThis ) in non-strict mode. Why: this
depends on the call-site, not the definition — unbound() is called with no receiver.
Q4.
Output: "1,2,34,5,6" Why: + on arrays triggers string coercion — each array becomes a comma-joined string, then concatenated.
Q5.
[Link](1)
.then((val) => { throw new Error("fail"); })
.catch((err) => [Link]("caught:", [Link]))
.then(() => [Link]("done"));
Output: caught: fail then done Why: .catch handles the thrown error, then the chain continues normally to the next .then .
Q6.
let a = { val: 1 };
let b = a;
[Link] = 2;
[Link]([Link]);
Output: 2 Why: Objects are assigned/passed by reference — a and b point to the same object in memory.
Q7.
[Link](1 + "1");
[Link](1 - "1");
[Link]("5" + 3);
[Link]("5" - 3);
Output: "11" , 0 , "53" , 2 Why: + prefers string concatenation if either operand is a string; - always forces numeric coercion.
Q8.
Output: start , A , end , B Why: Code before the first await runs synchronously; execution after await is scheduled as a microtask,
running after the synchronous code ( end ) finishes.
Q9.
function Person(name) {
[Link] = name;
}
const p1 = new Person("A");
const p2 = Person("B");
[Link]([Link]);
[Link](typeof p2);
Output: "A" then "undefined" Why: Without new , Person("B") runs as a plain function — this doesn’t bind to a new object, no
return value, and (in non-strict mode) it may leak name onto the global object.
Q10.
[Link]([..."hello"]);
[Link]([1, [2, [3, [4]]]].flat(Infinity));
Output: ["h","e","l","l","o"] then [1, 2, 3, 4] Why: Spread on a string iterates characters; .flat(Infinity) fully flattens nested
arrays regardless of depth.
Interview Tip for this chapter: When asked an output question, narrate your reasoning step-by-step (hoisting → execution order →
coercion rules) rather than blurting the answer — interviewers grade the process, not just correctness.
Chapter 4: [Link] Interview Questions
Q1. What is the Virtual DOM and how does React’s reconciliation work?
Answer: The Virtual DOM is a lightweight JS object representation of the real DOM. On state change, React builds a new virtual tree, diffs
it against the previous one (reconciliation), and applies only the minimal set of real-DOM updates. This is faster than direct DOM
manipulation because DOM operations are expensive, and batched minimal updates reduce reflow/repaint costs.
Follow-up: How does React’s diffing algorithm use key in lists? (Keys give elements stable identity across renders, letting React match
items instead of re-rendering the whole list.)
Tip: Never use array index as key when list order can change (reordering, insertion, deletion) — it causes state bugs and unnecessary re-
renders.
Answer: | Class Lifecycle | Hook Equivalent | |—|—| | constructor | useState initializer | | componentDidMount | useEffect(() =>
{...}, []) | | componentDidUpdate | useEffect(() => {...}, [dep]) | | componentWillUnmount | cleanup function returned from
useEffect |
useEffect(() => {
const timer = setInterval(() => [Link]("tick"), 1000);
return () => clearInterval(timer); // cleanup, like componentWillUnmount
}, []);
Answer: useState returns a stateful value and a setter. When new state depends on previous state, use the functional update form to
avoid stale-closure bugs, especially in loops or async callbacks.
Follow-up: Why does React batch state updates? (Performance — avoids multiple re-renders per event; React 18 extended batching to
promises/timeouts too via automatic batching.)
Answer: The dependency array tells React when to re-run the effect. Common mistakes: omitting a dependency (stale closures, bugs),
passing objects/functions recreated every render (causes infinite loops), or forgetting cleanup (memory leaks).
useEffect(() => {
fetchData(userId);
}, [userId]); // re-runs only when userId changes
Follow-up: How do useCallback / useMemo help fix dependency-array issues? (They memoize function/value references so they don’t
change every render, keeping the dependency stable.)
Tip: Use the eslint-plugin-react-hooks exhaustive-deps rule — most teams enforce it, and interviewers respect candidates who
mention it.
Answer: Both memoize across renders to avoid expensive recomputation, but useMemo memoizes a computed value, while
useCallback memoizes a function reference (equivalent to useMemo(() => fn, deps) ).
Tip: Don’t overuse these — memoization itself has a cost; only apply when profiling shows a real performance problem (expensive
computation or preventing child re-renders via [Link] ).
Answer: Custom Hooks are reusable functions (prefixed use ) that encapsulate stateful logic using built-in hooks, enabling logic reuse
across components without changing component hierarchy (unlike HOCs/render props).
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let ignore = false;
fetch(url).then(res => [Link]()).then(json => {
if (!ignore) { setData(json); setLoading(false); }
});
return () => { ignore = true; }; // avoid setting state after unmount
}, [url]);
Answer: Controlled components have their value driven by React state ( value + onChange ) — React is the single source of truth.
Uncontrolled components manage their own internal DOM state, accessed via ref when needed.
// Controlled
<input value={name} onChange={e => setName([Link])} />
// Uncontrolled
<input ref={inputRef} defaultValue="Sam" />
Tip: Most production forms use controlled components (or a library like React Hook Form, which uses uncontrolled inputs internally for
performance).
Answer: Keys help React identify which items changed, were added, or removed, enabling correct and efficient re-rendering. Keys must
be stable, unique among siblings — ideally a database ID, not array index.
Answer: Prop drilling is passing data through many intermediate components that don’t need it, just to reach a deeply nested child.
Solutions: Context API, state management libraries (Redux/Zustand), or component composition (passing children/JSX instead of raw data).
Answer: [Link] wraps a component to skip re-rendering if props haven’t changed (shallow comparison). It doesn’t help if props are
new objects/arrays/functions created every render (referentially different each time) — pair it with useMemo / useCallback on the parent to
keep prop references stable.
Q12. Explain the difference between SPA routing (React Router) concepts: BrowserRouter , dynamic routes,
protected routes.
Answer: BrowserRouter uses the HTML5 History API for clean URLs without full page reloads. Dynamic routes use path params
( /user/:id ) read via useParams() . Protected routes wrap a route element with an auth check, redirecting unauthenticated users.
Q13. Explain React’s reconciliation for conditional rendering — why does swapping component types remount?
Answer: React compares elements by type at the same tree position. If the type changes (e.g., <div> → <span> , or <ComponentA> →
<ComponentB> ), React tears down the old subtree completely (losing state) and mounts a fresh one — it does not attempt to diff across
different types.
Follow-up: How would you preserve state while conditionally showing/hiding a component? (Keep the same component type and toggle
visibility with CSS, or hoist state to a shared parent.)
Q14. What is server-side rendering (SSR) and why use [Link] with React?
Answer: SSR renders React components to HTML on the server per-request, sending fully-formed markup to the browser — improving
perceived performance (First Contentful Paint) and SEO, since crawlers see real content immediately. [Link] provides file-based routing,
built-in SSR/SSG/ISR, and API routes on top of React.
Follow-up: CSR vs SSR vs SSG — trade-offs? (CSR: simplest, slower first paint, poor SEO without extra work. SSR: fast first paint, higher
server cost, fresh data per request. SSG: pre-built at build time, fastest, but data can go stale until rebuild.)
Answer: Error boundaries are class components implementing static getDerivedStateFromError() and/or componentDidCatch() that
catch JS errors in their child tree during rendering, preventing the whole app from crashing to a blank screen.
Tip: Error boundaries do NOT catch errors in event handlers, async code, or SSR — mention this nuance to stand out.
Chapter 5: [Link] Interview Questions
Answer: [Link] is a JavaScript runtime built on Chrome’s V8 engine that executes JS outside the browser. It runs on a single main thread,
but I/O operations (file, network, DB) are delegated to the libuv thread pool / OS-level async APIs, and their callbacks are processed via
the event loop — so the main thread is never blocked waiting on I/O.
Follow-up: What happens if you run CPU-heavy synchronous code in Node? (It blocks the event loop, freezing all other requests — solved
with Worker Threads or offloading to another service/queue.)
Answer: Each loop iteration passes through phases: timers ( setTimeout / setInterval callbacks) → pending callbacks → poll (retrieve
new I/O events, execute I/O callbacks) → check ( setImmediate ) → close callbacks. Microtasks ( [Link] , Promises) run
between every phase transition, with [Link] having even higher priority than Promise microtasks.
Follow-up: Difference between setImmediate() and setTimeout(fn, 0) ? (Order isn’t guaranteed in the main module, but inside an I/O
callback, setImmediate always runs before a setTimeout scheduled there.)
Answer: require is synchronous, resolved at runtime, and caches modules by file path. ES Modules are statically analyzed (enabling
tree-shaking), asynchronous under the hood, and use "type": "module" in [Link] or .mjs extension.
// CommonJS
const fs = require("fs");
[Link] = myFunction;
// ESM
import fs from "fs";
export default myFunction;
Answer: Streams process data in chunks rather than loading everything into memory at once — critical for large files/network data. Four
types: Readable, Writable, Duplex (both), Transform (modifies data while passing through).
const fs = require("fs");
const readStream = [Link]("[Link]");
const writeStream = [Link]("[Link]");
[Link](writeStream); // memory-efficient copy
Follow-up: What’s backpressure in streams? (When a writable stream can’t consume data as fast as it’s produced — .pipe() handles
this automatically by pausing the readable stream.)
Q5. What is middleware in the context of Node/Express request handling? (bridges into Express — see Ch. 6)
Answer: Functions with access to (req, res, next) that execute during the request-response cycle, able to modify req / res , end the
cycle, or pass control via next() . Covered in depth in the Express chapter.
Answer: [Link]() queues a callback to run immediately after the current operation, before the event loop continues (highest
priority, can starve I/O if abused). setImmediate() runs in the “check” phase, after I/O callbacks in the current loop. setTimeout(fn, 0)
runs in the next “timers” phase, after at least the specified delay.
Q7. How do you handle errors in async [Link] code? (callbacks, promises, async/await)
Answer:
// Callback style — error-first convention
[Link]("[Link]", (err, data) => {
if (err) return [Link](err);
[Link](data);
});
Follow-up: How do you catch unhandled promise rejections globally? ( [Link]("unhandledRejection", handler) and
[Link]("uncaughtException", handler) as a last-resort safety net, though the process should still exit gracefully after logging.)
Answer: [Link] declares project metadata, dependencies (with semver ranges), and scripts. [Link] pins the exact
resolved version of every dependency (including transitive ones) so installs are reproducible across machines/CI.
Follow-up: Explain semver: ^1.2.3 vs ~1.2.3 . ( ^ allows minor + patch updates, ~ allows only patch updates.)
Q9. How would you scale a [Link] application across multiple CPU cores?
Answer: Node’s cluster module (or process managers like PM2) can fork multiple worker processes sharing the same server port, load-
balanced by the OS/master process, to fully use multi-core machines since a single Node process runs on one core.
Follow-up: How is this different from Worker Threads? (Cluster = multiple processes, separate memory, good for scaling stateless HTTP
servers. Worker Threads = threads within one process, sharing memory via SharedArrayBuffer , good for CPU-heavy tasks like image
processing.)
Q10. What are environment variables and how do you manage secrets in Node apps?
Answer: Environment variables ( [Link].X ) keep configuration/secrets out of source code. The dotenv package loads a .env file
into [Link] during development; in production, secrets are usually injected by the hosting platform or a secrets manager (AWS
Secrets Manager, Vault).
require("dotenv").config();
const dbUrl = [Link].MONGO_URI;
Tip: Always add .env to .gitignore — a very common interview/practical question about security hygiene.
Q11. Explain the difference between spawn , exec , and fork in Node’s child_process module.
Answer: spawn launches a new process and streams data (good for large output). exec buffers the entire output into a callback (good
for small commands, but risky for large output — buffer overflow). fork is a special case of spawn specifically for launching new [Link]
processes, with a built-in IPC channel for message passing.
Chapter 6: [Link] Interview Questions
Q1. What is [Link] and why use it over raw Node http ?
Answer: Express is a minimal web framework built on Node’s http module, providing routing, middleware chaining, request/response
helpers, and a large ecosystem — dramatically reducing boilerplate compared to hand-rolling routing/parsing logic with raw http .
Answer: Middleware are functions (req, res, next) executed in the order they’re registered. Types: application-level ( [Link] ),
router-level, built-in ( [Link]() , [Link]() ), error-handling (4 args: (err, req, res, next) ), and third-party (e.g.,
cors , morgan ).
Follow-up: What happens if you forget to call next() ? (Request hangs indefinitely — no response sent, no error thrown.)
Tip: Error-handling middleware must be registered LAST, after all routes, and must have exactly 4 parameters for Express to recognize it
as an error handler.
Answer:
Follow-up: How would you structure routes for a large app? (Split by resource into separate router files/modules, mounted with prefixes
— userRoutes , productRoutes , etc.)
Answer: Wrap async route handlers to forward errors to next(err) , then handle all errors in one final error-handling middleware,
avoiding repetitive try/catch in every route.
const asyncHandler = fn => (req, res, next) => [Link](fn(req, res, next)).catch(next);
Q6. How do you enable CORS in an Express API and why is it needed?
Answer: CORS (Cross-Origin Resource Sharing) is a browser security mechanism blocking requests to a different origin
(domain/port/protocol) unless the server explicitly allows it via response headers. In a MERN app, the React dev server (port 3000) and
Express API (port 5000) are different origins, so CORS must be configured.
Follow-up: Difference between simple requests and preflighted ( OPTIONS ) requests? (Preflight is triggered for non-simple
methods/headers like PUT , custom headers, or Content-Type: application/json — the browser sends an OPTIONS request first to check
permissions.)
Q7. How do you serve static files and structure a production-ready Express app?
Answer:
[Link]([Link]("public"));
// Common structure:
// /routes, /controllers, /models, /middleware, /config, /utils, [Link]
Separation of concerns: routes define endpoints, controllers hold business logic, models define data schemas, middleware handles cross-
cutting concerns (auth, logging, validation).
Q8. How would you implement rate limiting and basic security hardening in Express?
Answer: Use express-rate-limit to cap requests per IP/time window, and helmet to set secure HTTP headers (prevents clickjacking,
MIME sniffing, etc.). Also sanitize inputs against NoSQL injection with express-mongo-sanitize .
Tip: Mentioning helmet , rate limiting, and input sanitization together shows production-readiness awareness — a strong differentiator in
interviews.
Chapter 7: MongoDB & Mongoose Interview Questions
Q1. What is MongoDB and how does it differ from relational databases?
Answer: MongoDB is a document-oriented NoSQL database storing data as flexible, JSON-like BSON documents inside collections
(analogous to tables, but schema-less/schema-flexible). Unlike relational DBs, there’s no mandatory fixed schema or joins-by-default —
related data is often embedded within a single document for read performance, or referenced across collections when relationships are
many-to-many or data is large/shared.
Follow-up: When would you choose SQL over MongoDB? (Strong relational integrity needs, complex multi-table joins/transactions, strict
schema enforcement, well-defined reporting needs.)
Answer: - Embedding — nest related data directly inside a document. Fast reads (single query), but can bloat documents and duplicate
data (16MB document size limit). - Referencing — store an ObjectId reference to another document (like a foreign key), queried
separately or joined via $lookup / populate . Better for large, frequently-changing, or many-to-many related data.
Follow-up: Rule of thumb? (“Data that is read together should be stored together” — embed for data always accessed as a unit;
reference otherwise.)
Answer: Indexes are special data structures (B-trees) that speed up queries by avoiding full collection scans. Without an index, MongoDB
does a COLLSCAN ; with a matching index, it does an IXSCAN . Trade-off: indexes speed up reads but slow down writes (every insert/update
must also update the index) and consume extra storage.
Answer: The aggregation pipeline processes documents through a sequence of stages ( $match , $group , $sort , $project , $lookup ,
etc.), each transforming the data stream — similar to Unix pipes.
[Link]([
{ $match: { status: "completed" } },
{ $group: { _id: "$userId", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
]);
Follow-up: What does $lookup do? (Performs a left-outer-join to another collection, merging matched documents as an array field.)
Answer: Mongoose is an ODM (Object Data Modeling) library for MongoDB in [Link]. A Schema defines the shape/validation/types of
documents; a Model is a compiled constructor from a schema used to create/query/update documents.
Answer: Mongoose lets you run logic before/after certain operations ( save , validate , remove , find ) via pre / post hooks —
commonly used for password hashing before save, or logging after deletion.
Answer: populate() replaces a referenced ObjectId field with the actual referenced document(s) at query time, simulating a join.
Follow-up: Is populate() a real database join? (No — it performs a separate query behind the scenes and merges results in the
application layer, unlike a native SQL join.)
Q8. What are MongoDB transactions and when do you need them?
Answer: Transactions guarantee ACID properties across multiple operations/documents (even across collections), needed when several
writes must all succeed or all fail together (e.g., transferring money between two accounts). Available since MongoDB 4.0 with replica sets.
Answer: Mongoose schema validation runs at the application layer (before hitting the DB) — types, required , min / max , custom
validators. MongoDB also supports native JSON Schema validation at the collection level, enforced regardless of which application/driver
writes to it — useful when multiple services share one database.
Answer: Basic pagination uses skip() / limit() , but skip() gets slow on large offsets (must scan and discard N documents). For large
datasets, use cursor-based (keyset) pagination — filter on the last seen _id /sort field instead of skipping.
Answer: REST (Representational State Transfer) is an architectural style for APIs based on: statelessness (each request contains all
needed info, no server-side session), a uniform interface (resources identified by URLs, manipulated via standard HTTP methods), client-
server separation, and cacheability.
Follow-up: What does “idempotent” mean and why does it matter? (Calling it multiple times has the same effect as calling it once —
important for safe retries on network failure.)
Answer: - 200 OK — success. 201 Created — resource created. 204 No Content — success, no body. - 400 Bad Request — invalid
input. 401 Unauthorized — not authenticated. 403 Forbidden — authenticated but not allowed. 404 Not Found . 409 Conflict — e.g.,
duplicate resource. - 500 Internal Server Error — unexpected server failure. 503 Service Unavailable .
Tip: Confusing 401 and 403 is a very common mistake — 401 = “who are you?”, 403 = “I know who you are, but you can’t do that.”
Q3. Explain how JWT (JSON Web Token) authentication works end-to-end.
Answer: A JWT has 3 parts: [Link] . Flow: 1. User logs in with credentials. 2. Server verifies credentials, creates a JWT
signed with a secret key, containing claims (e.g., userId , exp ). 3. Server sends the token to the client (JSON body or httpOnly cookie). 4.
Client sends the token on subsequent requests ( Authorization: Bearer <token> ). 5. Server verifies the signature (no DB lookup needed
for basic verification) and extracts the user identity.
// Middleware to verify
function auth(req, res, next) {
const token = [Link]?.split(" ")[1];
if (!token) return [Link](401).json({ message: "No token" });
try {
[Link] = [Link](token, [Link].JWT_SECRET);
next();
} catch {
[Link](401).json({ message: "Invalid token" });
}
}
Follow-up: JWT vs session-based auth? (JWT is stateless — no server-side session store, scales horizontally easily, but harder to revoke
before expiry. Sessions are stateful, easy to revoke, but need shared session storage across servers.)
Answer: Access tokens are short-lived (minutes) to limit damage if leaked. A longer-lived refresh token (stored in an httpOnly, secure
cookie, and often in the DB for revocation) is used to silently obtain new access tokens without re-login. On logout or suspected
compromise, the refresh token is invalidated server-side.
Follow-up: Where should you store JWTs on the client, and why? (httpOnly cookies prevent JS/XSS access to the token; localStorage is
vulnerable to XSS but immune to CSRF — each has trade-offs, so combine httpOnly cookies with CSRF protection for best security.)
Answer: Never store plaintext passwords. Use a slow, salted hashing algorithm like bcrypt (or argon2), which automatically salts and is
intentionally slow to resist brute-force attacks.
const bcrypt = require("bcrypt");
const hashed = await [Link](plainPassword, 10); // 10 = salt rounds
const isMatch = await [Link](plainPassword, hashed);
Tip: Never use fast general-purpose hashes like MD5/SHA-256 alone for passwords — they’re too fast, making brute-forcing feasible.
Answer: Cross-Site Request Forgery tricks a logged-in user’s browser into submitting an unwanted request to your site (since cookies are
sent automatically). Mitigations: CSRF tokens (double-submit or synchronizer pattern), SameSite=Strict/Lax cookies, checking the
Origin / Referer header.
Answer: Common approaches: URL versioning ( /api/v1/users — simplest, most common), header versioning ( Accept:
application/[Link].v2+json ), or query param ( ?version=2 ). URL versioning is easiest to test/document and most widely used in
practice.
Q8. How do you design pagination, filtering, and sorting for a REST API?
Answer:
GET /api/products?page=2&limit=20&sort=-price&category=electronics
Return metadata alongside data: { data: [...], page, totalPages, totalCount } . Validate and cap limit server-side to prevent
abuse (e.g., someone requesting limit=1000000 ).
Chapter 9: SQL Basics (for MERN Developers)
Even in a MERN role, interviewers often test basic SQL to check general database literacy.
Answer: - INNER JOIN — only matching rows in both tables. - LEFT JOIN — all rows from the left table, matched rows (or NULL) from the
right. - RIGHT JOIN — mirror of LEFT JOIN. - FULL OUTER JOIN — all rows from both, matched where possible.
Answer: A Primary Key uniquely identifies each row in a table (not null, unique). A Foreign Key is a column referencing a Primary Key
in another table, enforcing referential integrity between tables.
Answer: Normalization organizes data to reduce redundancy and dependency issues. - 1NF — atomic columns, no repeating groups. -
2NF — 1NF + no partial dependency on a composite key. - 3NF — 2NF + no transitive dependency (non-key columns depend only on the
key).
Follow-up: When would you denormalize? (For read-heavy systems where join costs outweigh redundancy costs — common in
NoSQL/MongoDB embedding too.)
Answer: WHERE filters rows before grouping/aggregation; HAVING filters groups after GROUP BY /aggregation.
Answer: Like a book’s index, it lets the database find rows without scanning the whole table, speeding up SELECT / WHERE / JOIN queries
at the cost of slower INSERT / UPDATE / DELETE (index maintenance) and extra storage.
Answer: Atomicity — transaction is all-or-nothing. Consistency — DB moves between valid states. Isolation — concurrent transactions
don’t interfere. Durability — committed data survives crashes.
Answer:
Answer: merge combines branches by creating a new merge commit, preserving full history (non-destructive). rebase replays your
commits on top of another branch’s tip, producing a linear history — but rewrites commit hashes, so never rebase shared/public branches.
Tip: “Rebase locally to clean up history before pushing, merge for shared/public integration” is a well-liked rule of thumb.
Answer: Occurs when Git can’t automatically reconcile changes to the same lines/file across branches. Git marks conflicts with <<<<<<< ,
======= , >>>>>>> markers; you manually edit to the desired result, then git add the resolved file and continue the merge/rebase.
Answer: git reset moves the branch pointer backward, optionally altering the working directory/staging area ( --soft , --mixed , --
hard ) — rewrites history, risky on shared branches. git revert creates a new commit that undoes a previous commit’s changes — safe
for shared history since nothing is rewritten.
Q4. What is the difference between git fetch and git pull ?
Answer: git fetch downloads new commits/branches from remote without merging into your working branch. git pull = git fetch
+ git merge (or rebase with --rebase ) in one step.
Q5. Explain the typical Git branching workflow (Git Flow / feature branching) used in teams.
Answer: Common pattern: main (production-ready), develop (integration branch), feature/* branches off develop for new work,
merged via Pull Request after code review, hotfix/* branches off main for urgent production fixes. Many modern teams use a simpler
trunk-based approach with short-lived feature branches merged frequently via PRs.
Follow-up: What do you look for in a good Pull Request? (Small, focused diffs; clear description; passing CI/tests; no unrelated changes.)
Answer: Specifies files/folders Git should not track (e.g., node_modules/ , .env , build output) — keeps the repo clean, avoids leaking
secrets, and prevents bloating the repository with regenerable files.
Q7. How do you undo the last commit without losing changes?
Answer:
Answer: Typical setup: React frontend built ( npm run build ) into static assets, served via a CDN/static host (Vercel, Netlify) or served by
Express itself ( [Link] ); Node/Express backend deployed to a platform like Render, Railway, AWS EC2/Elastic Beanstalk, or
containerized with Docker; MongoDB hosted on MongoDB Atlas (managed cloud DB). Environment variables (DB URI, JWT secret) are set
via the hosting platform’s config, never committed to source.
Follow-up: How do you connect a React app (different origin) to your API in production? (Configure CORS on the backend for the exact
frontend domain, and use environment-based API base URLs — [Link].REACT_APP_API_URL — not hardcoded localhost.)
Q2. Explain the difference between environment configs (development, staging, production).
Answer: Each environment has its own config (DB connection, API keys, logging verbosity, debug flags) via environment variables — code
stays identical across environments (“build once, configure per environment” — a core 12-factor app principle).
Q3. What is a reverse proxy and why use Nginx in front of [Link]?
Answer: Nginx sits in front of the Node app to handle SSL/TLS termination, load balancing across multiple Node instances, serving static
files efficiently, gzip compression, and request buffering — offloading work Node isn’t optimized for.
Q4. What is CI/CD and how would you set it up for a MERN app?
Answer: Continuous Integration automatically runs tests/linting on every push/PR; Continuous Deployment automatically deploys passing
builds. Typical GitHub Actions pipeline: install deps → run tests → build React app → deploy to hosting platform (triggered on merge to
main ).
# .github/workflows/[Link] (simplified)
on: { push: { branches: [main] } }
jobs:
build-and-deploy:
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test && npm run build
- run: npm run deploy
Answer: Typically separate Dockerfiles for frontend and backend, orchestrated with docker-compose alongside a MongoDB container (or
use Atlas for managed DB in production).
Follow-up: Why use multi-stage builds for the frontend Docker image? (To keep the final image small — build stage has all
devDependencies and build tools, final stage only ships the compiled static files via a lightweight server like Nginx.)
Answer: Structured logging ( winston / pino ) instead of [Link] , centralized log aggregation (ELK stack, Datadog, CloudWatch),
application performance monitoring (New Relic, Sentry for error tracking), and health-check endpoints ( /health ) for uptime
monitoring/load balancers.
Chapter 12: Coding Round Questions (DSA + JavaScript Coding)
These are commonly asked as live-coding or take-home tasks in MERN interviews. Practice writing these without an IDE’s autocomplete.
function reverseString(str) {
let result = "";
for (let i = [Link] - 1; i >= 0; i--) {
result += str[i];
}
return result;
}
// reverseString("hello") -> "olleh"
function isPalindrome(str) {
const clean = [Link]().replace(/[^a-z0-9]/g, "");
return clean === [Link]("").reverse().join("");
}
function firstUniqueChar(str) {
const counts = {};
for (const ch of str) counts[ch] = (counts[ch] || 0) + 1;
for (const ch of str) if (counts[ch] === 1) return ch;
return null;
}
function flatten(arr) {
return [Link]((flat, item) =>
[Link]([Link](item) ? flatten(item) : item), []);
}
// flatten([1, [2, [3, 4]], 5]) -> [1, 2, 3, 4, 5]
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const num of arr) {
if ([Link](num)) [Link](num);
[Link](num);
}
return [...duplicates];
}
Q9. Group an array of objects by a property (common “practical” coding question, mirrors MongoDB $group ).
Q10. Implement a basic Promise-based retry wrapper for a flaky API call.
Tip: In live coding, always: (1) clarify edge cases out loud, (2) state time/space complexity, (3) write a couple of test cases before
declaring done.
Chapter 13: Project-Based Interview Questions
These questions probe how well you actually understand a project on your resume — practice a 2-minute verbal walkthrough of your main
MERN project before any interview.
Tip for answering: Structure your answer as: (1) problem it solves, (2) high-level architecture diagram in words (React frontend → REST
API → Express → MongoDB), (3) one interesting technical decision and why you made it, (4) one challenge and how you solved it. Keep it
under 2 minutes unless asked to go deeper.
What they’re testing: Whether you can justify a technical choice ( useState /Context vs Redux/Zustand) based on actual app
complexity, not just buzzwords.
Sample answer structure: “I used Context API for global auth state since it changes infrequently, but kept feature-specific state (e.g.,
shopping cart) local with useReducer for predictable updates, because Context would’ve caused unnecessary re-renders across the whole
app for frequently changing cart data.”
What they’re testing: Understanding of the full JWT flow, not just “I used JWT.” Be ready to describe: login endpoint, token storage
decision (cookie vs localStorage) and why, protected route middleware, token refresh/expiry handling, logout flow.
Q4. “How did you design your database schema, and why?”
What they’re testing: Real understanding of embedding vs referencing trade-offs (Chapter 7) applied to your actual data. Be ready to
justify one specific modeling decision you made and what you’d reconsider with more time/scale.
Q5. “What was the most challenging bug you fixed, and how did you debug it?”
Tip: Use a STAR-style structure (Situation, Task, Action, Result). Interviewers want to see your debugging process (reproducing the bug,
checking logs, isolating the cause, e.g., stale closures, race conditions, N+1 queries) more than the specific bug itself.
Q6. “How would you scale this project if traffic increased 100x?”
What they’re testing: Systems thinking beyond your current implementation. Mention: caching (Redis for frequent reads), database
indexing, horizontal scaling with load balancers, CDN for static assets, connection pooling, pagination on heavy endpoints, and moving
long-running tasks to background job queues.
Q7. “What would you do differently if you rebuilt this project today?”
What they’re testing: Self-awareness and growth mindset. A thoughtful, specific answer (e.g., “I’d add proper TypeScript types from the
start” or “I’d normalize my MongoDB schema differently to avoid data duplication”) lands far better than “nothing, it’s perfect.”
Q8. “How did you handle error states and loading states on the frontend?”
Sample points to cover: Loading skeletons/spinners, try/catch around API calls, user-friendly error messages (not raw error objects),
retry mechanisms, and an error boundary for unexpected crashes.
Chapter 14: HR & Behavioral Interview Questions
Q1. “Tell me about yourself.”
Tip: Use a brief Present → Past → Future structure: current role/skills (MERN stack focus) → relevant past experience/projects → why you’re
excited about this specific opportunity. Keep it to 60-90 seconds, and tailor the “future” part to the actual company/role.
Tip: Research the company beforehand — mention something specific (their product, tech stack, engineering culture, or a recent
development) rather than generic praise. Avoid answers that are purely about salary/benefits.
Tip: Connect 2-3 concrete skills/experiences directly to the job description’s requirements. Be specific and quantify impact where possible
(“reduced API response time by X%”, “built and shipped Y feature”).
Tip: Use STAR format. Focus on how you communicated respectfully, sought to understand their perspective, and reached a resolution —
avoid badmouthing anyone; show emotional maturity and collaboration.
Tip: Own the mistake honestly, then pivot to what you learned and changed afterward (a process improvement, better estimation, earlier
communication). Never blame others entirely; interviewers want evidence of growth, not perfection.
Tip: Show ambition aligned with growing technically (e.g., deeper system design skills, mentoring others) while staying realistic and
relevant to the career path this role offers.
Tip: Give concrete examples — specific blogs, changelogs you follow, side projects, open-source contributions, or courses — generic
answers (“I read articles”) are forgettable.
Tip: ALWAYS have 2-3 questions ready — this is scored. Good ones: “What does the onboarding/mentorship process look like for new
engineers?”, “What’s the biggest technical challenge the team is currently facing?”, “How is the engineering team structured, and how do
MERN projects typically move from planning to production here?”
Common mistake to avoid: Asking only about salary/perks in the first round, or saying “No, I think you covered everything” — always
ask something.
Chapter 15: Final Checklist & Interview Day Tips
Night Before
Re-read Chapter 3 (JS output questions) — highest ROI for screening rounds
Rehearse your 2-minute project walkthrough out loud, once
Review your resume — be ready to explain every technology and project listed
Prepare 3 questions to ask the interviewer (Chapter 14, Q8)
Get a full night’s sleep — pattern recall drops sharply when tired
Morning Of
Skim this guide’s chapter headers as a memory trigger, not a full re-read
Test your camera/mic/internet if it’s a remote interview, 15 minutes early
Have a pen and paper ready for system design/whiteboard-style questions
Keep a glass of water nearby
Think out loud. Interviewers grade your reasoning process, not just the final answer — especially for coding and output-based
questions.
Clarify before coding. Ask about edge cases, input constraints, and expected output format before writing code.
It’s okay to say “I don’t know, but here’s how I’d find out.” Honesty plus a reasonable approach beats confident guessing.
Relate answers to your projects wherever possible — it’s far more convincing than purely theoretical answers.
Watch the clock on take-home/live coding rounds — a working, simpler solution beats an unfinished complex one.
1. Memorizing answers word-for-word instead of understanding the underlying concept — interviewers can tell, and follow-up questions
will expose gaps.
2. Not testing your code with at least one example before saying “done.”
3. Badmouthing a previous employer, teammate, or codebase.
4. Forgetting to ask the interviewer any questions at the end.
5. Rambling — aim for concise, structured answers (2 minutes max unless asked to elaborate).
Good luck with your interview! Revisit this guide’s summaries a day before, and focus extra revision time on whichever chapters you feel
least confident in.