From 9af7f0a94f11bcf5cb998fe1e2b5dcd42488d302 Mon Sep 17 00:00:00 2001 From: Shubham kahar Date: Tue, 11 Aug 2026 08:55:53 +0530 Subject: [PATCH] MERN Interview Question --- MERN_INTERVIEW_QUESTION.md | 3969 ++++++++++++++++++++++++++++++++++++ 1 file changed, 3969 insertions(+) create mode 100644 MERN_INTERVIEW_QUESTION.md diff --git a/MERN_INTERVIEW_QUESTION.md b/MERN_INTERVIEW_QUESTION.md new file mode 100644 index 0000000..722ffda --- /dev/null +++ b/MERN_INTERVIEW_QUESTION.md @@ -0,0 +1,3969 @@ +# πŸš€ MERN Interview Sheet (2026 Edition) + +> The only 100 questions you need to confidently crack most MERN Stack interviews in 2026. + +**Resource:** [MERN Interview Sheet 2026 Edition](https://bloom-dewberry-5ad.notion.site/MERN-Interview-Sheet-2026-Edition-3918ff713cb080578b70d320d1326693) + +--- + +## πŸ“‹ Table of Contents + +- [πŸ“Œ JavaScript (20)](#javascript-20) +- [βš›οΈ React (25)](#react-25) +- [🟒 Node.js & Express (18)](#nodejs--express-18) +- [πŸƒ MongoDB (15)](#mongodb-15) +- [πŸ” Authentication & Security (10)](#authentication--security-10) +- [🐳 Deployment & DevOps (5)](#deployment--devops-5) +- [πŸ—οΈ System Design (7)](#system-design-7) +- [πŸ“š Project Questions (Bonus)](#project-questions-bonus) + +--- + +## πŸ“Œ JavaScript (20) + +### 1. Difference between var, let, and const. + +- `var` is function-scoped, can be redeclared and reassigned, and is hoisted with an initial value of `undefined`. +- `let` is block-scoped, can be reassigned but cannot be redeclared in the same scope. It is hoisted but remains in the Temporal Dead Zone (TDZ) until its declaration. +- `const` is also block-scoped and must be initialized during declaration. It cannot be reassigned, but if it stores an object or array, the object's contents can still be modified. + +**Example** + +```javascript +var a = 10; +var a = 20; // βœ… Allowed + +let b = 10; +// let b = 20; ❌ Error + +const c = 10; +// c = 20; ❌ Error + +const obj = { name: "John" }; +obj.name = "Alex"; // βœ… Allowed +``` + +--- + +### 2. What is Hoisting? + +- Hoisting is JavaScript's behavior where variable and function declarations are processed before code execution. +- `var` is hoisted and initialized with `undefined`. +- `let` and `const` are hoisted but remain in the Temporal Dead Zone (TDZ) until execution reaches their declaration. +- Function declarations are completely hoisted and can be called before they appear in the code. + +**Example** + +```javascript +console.log(a); // undefined +var a = 10; + +console.log(b); // ReferenceError +let b = 20; + +sayHello(); // Works + +function sayHello() { + console.log("Hello"); +} +``` + +--- + +### 3. What is the Temporal Dead Zone (TDZ)? + +- The Temporal Dead Zone (TDZ) is the period between entering a scope and the point where a `let` or `const` variable is declared. +- Although `let` and `const` are hoisted, they cannot be accessed before their declaration. Attempting to do so throws a `ReferenceError`. +- The TDZ was introduced to prevent developers from using variables before they are initialized, making JavaScript safer and reducing bugs. + +**Example** + +```javascript +console.log(a); // ❌ ReferenceError + +let a = 10; +``` + +--- + +### 4. What are Closures? + +A Closure is a function that remembers variables from its outer lexical scope even after the outer function has finished executing. + +Normally, local variables are destroyed when a function completes. However, if an inner function references those variables, JavaScript keeps them alive in memory. This allows the inner function to access them whenever it is called. + +Closures are commonly used for: + +- Private variables +- Data encapsulation +- Debouncing +- Throttling + +**Example** + +```javascript +function outer() { + let count = 0; + + return function inner() { + count++; + console.log(count); + }; +} + +const counter = outer(); + +counter(); // 1 +counter(); // 2 +counter(); // 3 +``` + +Even though `outer()` has finished executing, the `inner()` function still has access to the `count` variable because of the closure. + +**Real-world Example (Private Variable)** + +```javascript +function createBankAccount() { + let balance = 1000; + + return { + deposit(amount) { + balance += amount; + }, + + getBalance() { + return balance; + } + }; +} + +const account = createBankAccount(); + +account.deposit(500); + +console.log(account.getBalance()); // 1500 +``` + +Here, `balance` cannot be accessed directly from outside, making it a private variable. + +--- + +### 5. What is Lexical Scope? + +Lexical Scope means a function can access variables based on where it is defined in the source code, not where it is called. + +An inner function can access: + +- Its own variables. +- Variables from its parent function. +- Variables from the global scope. + +However, an outer function cannot access variables declared inside an inner function. + +Lexical scope is the foundation of Closures because closures rely on the scope where a function was created. + +**Example** + +```javascript +const name = "Global"; + +function outer() { + const city = "Delhi"; + + function inner() { + console.log(name); // Global + console.log(city); // Delhi + } + + inner(); +} + +outer(); +``` + +The `inner()` function can access both `name` and `city` because they are in its lexical scope. + +**Example showing outer function cannot access inner variables** + +```javascript +function outer() { + function inner() { + let age = 22; + } + + inner(); + + console.log(age); // ❌ ReferenceError +} + +outer(); +``` + +The `age` variable belongs only to `inner()`, so `outer()` cannot access it. + +--- + +### 6. Difference between Lexical Scope and Closure + +| Lexical Scope | Closure | +| --- | --- | +| Determines how variables are resolved based on where functions are written. | Allows a function to remember variables from its lexical scope after the outer function has finished executing. | +| Exists in every JavaScript program. | Created only when an inner function references variables from its outer scope. | +| Defines accessibility of variables. | Preserves those variables for later use. | + +--- + +> πŸ’‘ **Interview Tip:** If asked "What is the relationship between Lexical Scope and Closure?", you can answer: *"Lexical scope determines which variables a function can access based on where it is defined, while a closure is created when a function remembers those variables even after the outer function has finished executing. In simple terms, lexical scope makes closures possible."* + +--- + +### 7. Explain the JavaScript Event Loop. + +JavaScript is single-threaded, meaning it can execute only one task at a time. The Event Loop is the mechanism that allows JavaScript to perform asynchronous operations like API calls, timers, and event handling without blocking the main thread. + +When synchronous code is executing, it runs inside the Call Stack. Asynchronous operations are handled by the browser (or Node.js APIs). Once they are completed, their callbacks are placed into either the Microtask Queue or the Callback Queue. The Event Loop continuously checks whether the Call Stack is empty. If it is, it first executes all Microtasks and then processes tasks from the Callback Queue. + +This is how JavaScript remains non-blocking even though it has only one thread. + +**Example** + +```javascript +console.log("Start"); + +setTimeout(() => { + console.log("Timeout"); +}, 0); + +Promise.resolve().then(() => { + console.log("Promise"); +}); + +console.log("End"); +``` + +**Output:** + +```text +Start +End +Promise +Timeout +``` + +**Reason:** + +- `Start` β†’ Call Stack +- `End` β†’ Call Stack +- `Promise` callback β†’ Microtask Queue +- `setTimeout` callback β†’ Callback Queue +- Event Loop executes Microtasks first, then Callback Queue. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why is JavaScript asynchronous if it is single-threaded?" *JavaScript itself is single-threaded, but asynchronous operations are handled by browser APIs or Node.js APIs. Once completed, their callbacks are placed into queues, and the Event Loop moves them to the Call Stack when it becomes empty.* + +--- + +### 8. What is the difference between the Call Stack, Callback Queue, and Microtask Queue? + +| Concept | Description | +| --- | --- | +| **Call Stack** | The Call Stack is where JavaScript executes synchronous code. It follows the Last In, First Out (LIFO) principle. | +| **Callback Queue** | The Callback Queue (Macrotask Queue) stores callbacks from APIs such as `setTimeout`, `setInterval`, DOM events, and network requests. | +| **Microtask Queue** | The Microtask Queue stores higher-priority tasks such as Promise callbacks, `queueMicrotask()`, and `MutationObserver`. | + +> Whenever the Call Stack becomes empty, the Event Loop first executes all Microtasks, and only then executes tasks from the Callback Queue. + +**Example** + +```javascript +console.log("A"); + +setTimeout(() => { + console.log("B"); +}, 0); + +Promise.resolve().then(() => { + console.log("C"); +}); + +console.log("D"); +``` + +**Output:** + +```text +A +D +C +B +``` + +**Remember this order:** + +```text +Call Stack + ↓ +Microtask Queue + ↓ +Callback Queue +``` + +--- + +### 9. Explain the lifecycle of a Promise. + +A Promise is an object that represents the eventual completion or failure of an asynchronous operation. + +A Promise has three states: + +- **Pending** – Initial state while the asynchronous task is running. +- **Fulfilled** – The operation completed successfully. +- **Rejected** – The operation failed with an error. + +Once a Promise becomes Fulfilled or Rejected, it is considered settled, and its state cannot change again. + +Promises help avoid callback hell and make asynchronous code easier to manage using `.then()`, `.catch()`, and `.finally()`. + +**Example** + +```javascript +const promise = new Promise((resolve, reject) => { + let success = true; + + if (success) { + resolve("Success"); + } else { + reject("Failed"); + } +}); + +promise + .then(result => console.log(result)) + .catch(error => console.log(error)) + .finally(() => console.log("Completed")); +``` + +**Promise Lifecycle** + +```text +Pending + β”‚ + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β–Ί Fulfilled + β”‚ + └────────► Rejected +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "Can a settled Promise change its state?" *No. Once a Promise becomes Fulfilled or Rejected, it is settled permanently and its state cannot change.* + +--- + +### 10. How does async/await work internally? + +`async/await` is syntactic sugar built on top of Promises. It provides a cleaner way to write asynchronous code that looks like synchronous code. + +An `async` function always returns a Promise, even if it returns a normal value. The `await` keyword pauses the execution of the current async function until the awaited Promise settles. During this pause, JavaScript does not block the thread. Instead, the Event Loop continues executing other tasks, and once the Promise resolves, execution resumes from the point after `await`. + +**Example** + +```javascript +function fetchData() { + return new Promise(resolve => { + setTimeout(() => resolve("Data Loaded"), 1000); + }); +} + +async function getData() { + console.log("Loading..."); + + const data = await fetchData(); + + console.log(data); +} + +getData(); +``` + +**Output:** + +```text +Loading... +Data Loaded +``` + +**Internally** + +```javascript +async function getData() { + const data = await fetchData(); +} +``` + +is similar to + +```javascript +function getData() { + return fetchData().then(data => { + // continue execution + }); +} +``` + +--- + +### 11. What is Callback Hell? How can it be avoided? + +Callback Hell occurs when multiple asynchronous operations are nested inside one another, creating deeply indented code that becomes difficult to read, debug, and maintain. This nested structure is often called the **Pyramid of Doom**. + +Callback Hell was common before Promises and async/await were introduced. It can be avoided by using Promises, async/await, breaking code into smaller functions, and handling errors with `.catch()` or `try...catch`. + +**Callback Hell Example** + +```javascript +getUser(function(user) { + getOrders(user.id, function(orders) { + getPayment(orders[0], function(payment) { + console.log(payment); + }); + }); +}); +``` + +This code becomes increasingly difficult to understand as more asynchronous operations are added. + +**Better with async/await** + +```javascript +async function getPaymentDetails() { + try { + const user = await getUser(); + const orders = await getOrders(user.id); + const payment = await getPayment(orders[0]); + + console.log(payment); + } catch (error) { + console.log(error); + } +} +``` + +The logic is the same, but the code is much cleaner, easier to read, and simpler to maintain. + +--- + +> πŸ’‘ **Interview Tip:** If asked "How do you avoid Callback Hell?" *I avoid Callback Hell by using Promises and async/await instead of deeply nested callbacks. I also split complex logic into smaller functions and use proper error handling with try/catch or `.catch()` to keep asynchronous code clean and maintainable.* + +--- + +### 12. What is Debouncing? Where would you use it? + +Debouncing is a technique that delays the execution of a function until a specified amount of time has passed since the last event occurred. + +If the event keeps occurring before the delay expires, the timer resets, and the function executes only once after the user stops triggering the event. This prevents unnecessary function calls, improves performance, and reduces server load. + +Debouncing is commonly used for: + +- Search input (API calls) +- Auto-save functionality +- Form validation +- Window resize events + +**Example** + +```javascript +function debounce(fn, delay) { + let timer; + + return function (...args) { + clearTimeout(timer); + + timer = setTimeout(() => { + fn.apply(this, args); + }, delay); + }; +} + +function search(query) { + console.log("Searching:", query); +} + +const debounceSearch = debounce(search, 500); + +// Called on every key press +debounceSearch("R"); +debounceSearch("Re"); +debounceSearch("Rea"); +debounceSearch("React"); +``` + +**Output:** + +```text +Searching: React +``` + +Only the last call executes because each new call resets the timer. + +**Real-world Example** + +Suppose a user types "React Developer" into a search box. + +**Without Debouncing:** + +```text +R +Re +Rea +Reac +React +... + +πŸ‘‰ 15 API requests +``` + +**With Debouncing (500ms):** + +```text +React Developer + +πŸ‘‰ Only 1 API request +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why use Debouncing?" *Debouncing prevents a function from executing repeatedly by waiting until the user stops triggering the event. It's mainly used to optimize expensive operations like search API calls, form validation, and resize events.* + +--- + +### 13. What is Throttling? Where would you use it? + +Throttling is a technique that ensures a function executes at most once within a specified time interval, regardless of how many times the event occurs. + +Unlike Debouncing, Throttling does not wait for the event to stop. Instead, it limits the execution rate, making it useful for continuously firing events. + +Throttling is commonly used for: + +- Scroll events +- Mouse movement +- Window resize +- Infinite scrolling +- Button click prevention + +**Example** + +```javascript +function throttle(fn, delay) { + let lastCall = 0; + + return function (...args) { + const now = Date.now(); + + if (now - lastCall >= delay) { + lastCall = now; + fn.apply(this, args); + } + }; +} + +function logScroll() { + console.log("Scrolling..."); +} + +const throttledScroll = throttle(logScroll, 1000); + +window.addEventListener("scroll", throttledScroll); +``` + +Even if the user scrolls hundreds of times, the function runs only once every second. + +--- + +### 14. Follow-up Question: Debounce vs Throttle + +| Debounce | Throttle | +| --- | --- | +| Executes after the event stops | Executes at fixed intervals | +| Resets timer on every event | Ignores extra events until interval ends | +| Best for search inputs | Best for scrolling and resizing | +| Reduces unnecessary API calls | Limits execution frequency | + +--- + +> πŸ’‘ **Interview Tip:** If asked "When would you choose Throttling over Debouncing?" *I'd use Throttling when I need regular updates during continuous events like scrolling or mouse movement. I'd use Debouncing when I only care about the final event, such as a user finishing typing in a search box.* + +--- + +### 15. What is the difference between Shallow Copy and Deep Copy? + +**Shallow Copy** copies only the first level of an object or array. If the original object contains nested objects or arrays, the copy still references the same nested data. Therefore, changing nested properties affects both the original and copied objects. + +**Shallow Copy Example** + +```javascript +const user = { + name: "John", + address: { + city: "Delhi" + } +}; + +const copy = { ...user }; + +copy.address.city = "Mumbai"; + +console.log(user.address.city); // Mumbai +``` + +Both objects share the same nested `address` object. + +**Deep Copy** creates a completely independent copy, including all nested objects and arrays. Changes made to the copied object do not affect the original object. + +**Deep Copy Example** + +```javascript +const user = { + name: "John", + address: { + city: "Delhi" + } +}; + +const copy = structuredClone(user); + +copy.address.city = "Mumbai"; + +console.log(user.address.city); // Delhi +``` + +The original object remains unchanged because `structuredClone()` creates a true deep copy. + +--- + +### 16. Difference + +| Shallow Copy | Deep Copy | +| --- | --- | +| Copies only first level | Copies every level | +| Nested objects share references | Nested objects are independent | +| Spread operator, `Object.assign()` | Slightly slower, `structuredClone()` | + +--- + +> πŸ’‘ **Interview Tip:** If asked "Does the spread operator create a deep copy?" *No. The spread operator creates only a shallow copy. Nested objects and arrays still share the same references.* + +--- + +### 17. What is the difference between == and ===? + +`==` is the loose equality operator. It compares values after performing type coercion, meaning JavaScript automatically converts operands to compatible types before comparison. + +`===` is the strict equality operator. It compares both the value and the data type without performing type conversion. + +Because it avoids unexpected type coercion, `===` is recommended in modern JavaScript. + +**Example** + +```javascript +console.log(5 == "5"); // true +console.log(5 === "5"); // false + +console.log(null == undefined); // true +console.log(null === undefined); // false +``` + +--- + +### 18. Explain the `this` keyword in JavaScript. + +The `this` keyword refers to the object that is currently executing the function. Its value is determined at runtime based on how the function is called, not where it is defined (except for arrow functions). + +The value of `this` varies depending on the execution context: + +- In a regular object method, `this` refers to the object that called the method. +- In the global scope, `this` refers to the global object (`window` in browsers) or `undefined` in ES modules and strict mode. +- Inside a constructor function or class, `this` refers to the newly created instance. +- Arrow functions do not have their own `this`; they inherit it from their surrounding lexical scope. + +**Example** + +```javascript +const user = { + name: "John", + + greet() { + console.log(this.name); + } +}; + +user.greet(); // John +``` + +**Arrow Function Example** + +```javascript +const user = { + name: "John", + + greet: () => { + console.log(this.name); + } +}; + +user.greet(); // undefined +``` + +The arrow function doesn't have its own `this`, so it uses the surrounding scope instead of the `user` object. + +--- + +> πŸ’‘ **Interview Tip:** If asked "How is `this` determined?" *In JavaScript, `this` is determined by how a function is invoked. Regular functions get their own `this` based on the caller, while arrow functions inherit `this` from their enclosing lexical scope.* + +--- + +### 19. What are Arrow Functions? How are they different from regular functions? + +Arrow Functions are a shorter syntax for writing functions, introduced in ES6. They provide cleaner code and do not have their own `this`, instead inheriting `this` from the surrounding lexical scope. + +Arrow functions are commonly used for callbacks, array methods (`map`, `filter`, `reduce`), and React functional components. However, they should not be used as object methods or constructors because they lack their own `this`, `arguments`, and cannot be called with `new`. + +**Example** + +```javascript +// Regular Function +function add(a, b) { + return a + b; +} + +// Arrow Function +const add = (a, b) => a + b; +``` + +--- + +### 20. Difference between Regular Function and Arrow Function + +> πŸ’‘ **Interview Tip:** If asked "When should you avoid Arrow Functions?" *Avoid using arrow functions as object methods, constructors, or when you need your own `this` or `arguments` object. They are best suited for callbacks and short utility functions.* + +--- + +## βš›οΈ React (25) + +### 1. What is useRef used for? + +`useRef` is a React Hook that creates a mutable object whose value persists across renders without causing a re-render when it changes. + +It is commonly used to access DOM elements directly, store mutable values such as timers or previous state, and integrate with third-party libraries. Unlike `useState`, updating a ref does not trigger a component re-render. + +**Syntax** + +```javascript +const ref = useRef(initialValue); +``` + +**Accessing a DOM Element** + +```javascript +import { useRef } from "react"; + +function App() { + const inputRef = useRef(); + + function handleClick() { + inputRef.current.focus(); + } + + return ( + <> + + + + + ); +} +``` + +**Storing Mutable Values** + +```javascript +const renderCount = useRef(0); + +renderCount.current++; +``` + +Updating `renderCount.current` does not re-render the component. + +**Common Use Cases** + +- Focusing an input +- Scrolling to an element +- Playing videos +- Storing timer IDs +- Storing previous state +- Integrating third-party libraries + +**Difference between useRef and useState** + +| useRef | useState | +| --- | --- | +| Doesn't trigger re-render | Triggers re-render | +| Stores mutable value | Stores UI state | +| Used for DOM access | Used for rendering UI | +| Value stored in `.current` | Value stored directly | + +--- + +> πŸ’‘ **Interview Tip:** If asked "When should you use useRef instead of useState?" *"Use useRef when you need to store a value that persists across renders but doesn't affect the UI, such as DOM references, timers, or previous values. Use useState when changing the value should update the UI."* + +--- + +### 2. When should you use useMemo? + +`useMemo` is a React Hook used to memoize expensive calculations. It caches the computed result and recomputes it only when one of its dependencies changes. + +Without `useMemo`, expensive calculations run on every render, even if the inputs haven't changed. Using `useMemo` can improve performance, especially when working with large datasets or computationally expensive logic. + +**Syntax** + +```javascript +const memoizedValue = useMemo(() => { + return expensiveCalculation(); +}, [dependencies]); +``` + +**Example** + +```javascript +import { useMemo } from "react"; + +function App({ numbers }) { + const total = useMemo(() => { + console.log("Calculating..."); + + return numbers.reduce((sum, num) => sum + num, 0); + }, [numbers]); + + return

{total}

; +} +``` + +The calculation runs only when `numbers` changes. + +**Common Use Cases** + +- Large array filtering +- Sorting data +- Complex calculations +- Data transformation +- Expensive computations + +**Difference between without and with useMemo** + +**Without useMemo** + +```javascript +const total = expensiveCalculation(data); +``` + +Runs every render. + +**With useMemo** + +```javascript +const total = useMemo(() => expensiveCalculation(data), [data]); +``` + +Runs only when `data` changes. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Should you wrap every calculation in useMemo?" *"No. useMemo itself has a small overhead. It should only be used for expensive calculations where avoiding recomputation provides a measurable performance benefit."* + +--- + +### 3. When should you use useCallback? + +`useCallback` is a React Hook that memoizes a function. It returns the same function instance between renders unless one of its dependencies changes. + +This is useful when passing functions to child components wrapped with `React.memo()`, because creating a new function on every render can cause unnecessary child re-renders. + +**Syntax** + +```javascript +const memoizedFunction = useCallback(() => { + // Function +}, [dependencies]); +``` + +**Example** + +```javascript +import { useCallback } from "react"; + +function Parent() { + const handleClick = useCallback(() => { + console.log("Clicked"); + }, []); + + return ; +} +``` + +The same function reference is reused across renders. + +**Common Use Cases** + +- Passing callbacks to child components +- Event handlers +- Preventing unnecessary re-renders +- Working with `React.memo` + +**Difference between useMemo and useCallback** + +| useMemo | useCallback | +| --- | --- | +| Memoizes a value | Memoizes a function | +| Returns computed value | Returns function | +| Optimizes expensive calculations | Optimizes function references | + +--- + +> πŸ’‘ **Interview Tip:** If asked "When should you use useCallback?" *"I use useCallback when passing callback functions to memoized child components or when a stable function reference is required. It helps prevent unnecessary child re-renders."* + +--- + +### 4. When should you use useReducer? + +`useReducer` is a React Hook used for managing complex state logic. It is an alternative to `useState` when state updates depend on previous state, involve multiple related values, or require complex transitions. + +Instead of updating state directly, `useReducer` uses a reducer function that receives the current state and an action, then returns the new state. + +It follows the same pattern used by Redux. + +**Syntax** + +```javascript +const [state, dispatch] = useReducer(reducer, initialState); +``` + +**Example** + +```javascript +import { useReducer } from "react"; + +function reducer(state, action) { + switch (action.type) { + case "increment": + return state + 1; + + case "decrement": + return state - 1; + + default: + return state; + } +} + +function Counter() { + const [count, dispatch] = useReducer(reducer, 0); + + return ( + <> +

{count}

+ + + + + + ); +} +``` + +**When to use useReducer** + +- Complex state updates +- Multiple related state values +- State transitions +- Forms +- Shopping carts +- Authentication +- Large applications + +**Difference between useState and useReducer** + +| useState | useReducer | +| --- | --- | +| Simple state | Complex state | +| Direct setter | Dispatch actions | +| Less boilerplate | More structured | +| Small components | Large components | + +--- + +> πŸ’‘ **Interview Tip:** If asked "When would you choose useReducer over useState?" *"I use useReducer when state logic becomes complex, involves multiple related values, or requires predictable state transitions. It centralizes update logic and makes the code easier to maintain."* + +--- + +### 5. What is Context API? + +The Context API is React's built-in state-sharing mechanism that allows data to be passed through the component tree without manually passing props at every level. + +It helps solve **Prop Drilling**, where props must be passed through multiple intermediate components even if they don't use them. + +A Context consists of three main parts: + +- `createContext()` – Creates a Context object. +- `Provider` – Supplies the value to descendant components. +- `useContext()` – Consumes the Context value. + +**Without Context (Prop Drilling)** + +```text +App + β”‚ + β–Ό +Navbar + β”‚ + β–Ό +Sidebar + β”‚ + β–Ό +Profile +``` + +The same props are passed through every component. + +**With Context API** + +```text +Provider + β”‚ + β–Ό +Any Child Component +``` + +Any child inside the Provider can access the shared data directly. + +**Example β€” Create Context** + +```javascript +import { createContext } from "react"; + +export const UserContext = createContext(); +``` + +**Provide Value** + +```javascript + + + +``` + +**Consume Value** + +```javascript +import { useContext } from "react"; + +function Profile() { + const user = useContext(UserContext); + + return

{user}

; +} +``` + +**Common Use Cases** + +- Theme (Dark/Light) +- Authentication +- Language selection +- User profile +- Global settings + +**Drawbacks** + +- Frequent context updates can re-render all consuming components. +- Not ideal for highly complex global state management. +- Better suited for lightweight shared state. + +--- + +> πŸ’‘ **Interview Tip:** If asked "When should you use Context API instead of Redux?" *"I use Context API for simple global state like themes, authentication, or language settings. For complex applications with frequent updates, advanced debugging, and predictable state management, I prefer Redux Toolkit."* + +--- + +Wednesday 4:49 PM + +next 5 + +--- + +### 6. Why use Redux Toolkit? + +Redux Toolkit (RTK) is the official, recommended way to write Redux applications. It reduces boilerplate code, simplifies state management, and includes best practices by default. + +Traditional Redux requires creating actions, action types, reducers, and store configuration manually. Redux Toolkit simplifies this by providing utilities like `createSlice()`, `configureStore()`, and `createAsyncThunk()`. + +Redux Toolkit also uses Immer internally, allowing developers to write mutable-looking code while keeping the state immutable. + +**Traditional Redux** + +```text +Action + β”‚ + β–Ό +Reducer + β”‚ + β–Ό +Store +``` + +Lots of boilerplate code. + +**Redux Toolkit** + +```text +createSlice() + β”‚ + β–Ό +Reducer + Actions + β”‚ + β–Ό +configureStore() +``` + +Much simpler and cleaner. + +**Example β€” Slice** + +```javascript +import { createSlice } from "@reduxjs/toolkit"; + +const counterSlice = createSlice({ + name: "counter", + + initialState: { + value: 0, + }, + + reducers: { + increment(state) { + state.value++; + }, + + decrement(state) { + state.value--; + }, + }, +}); + +export const { increment, decrement } = counterSlice.actions; + +export default counterSlice.reducer; +``` + +**Common Features** + +- Less boilerplate +- Built-in Immer +- Better TypeScript support +- DevTools integration +- Async support using `createAsyncThunk()` +- Official Redux recommendation + +**Difference between Redux and Redux Toolkit** + +| Redux | Redux Toolkit | +| --- | --- | +| More boilerplate | Less boilerplate | +| Manual store setup | `configureStore()` | +| Manual actions | `createSlice()` | +| Manual immutable updates | Immer handles immutability | +| More complex | Easier to learn | + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why do modern React applications use Redux Toolkit instead of Redux?" *"Redux Toolkit is the official Redux approach because it reduces boilerplate, simplifies store configuration, automatically generates actions and reducers, and uses Immer for immutable state updates."* + +--- + +### 7. What is React.memo()? + +`React.memo()` is a Higher-Order Component (HOC) that memoizes a functional component. It prevents the component from re-rendering if its props have not changed. + +Normally, when a parent component re-renders, all of its child components also re-render. Wrapping a component with `React.memo()` tells React to skip rendering that component when its props remain the same. + +It is mainly used to optimize performance in applications with expensive rendering. + +**Syntax** + +```javascript +const MemoizedComponent = React.memo(Component); +``` + +**Example** + +```javascript +const Child = React.memo(function Child({ name }) { + console.log("Child Render"); + + return

{name}

; +}); +``` + +If `name` doesn't change, `Child` won't render again. + +**Common Use Cases** + +- Large component trees +- Dashboard applications +- Expensive UI rendering +- Data tables +- Lists + +**When does React.memo NOT work?** + +If props are objects or functions recreated on every render. + +```javascript + +``` + +This object is recreated every render. Use `useMemo()` or `useCallback()` to keep references stable. + +**React.memo + useCallback** + +```javascript +const handleClick = useCallback(() => { + console.log("Click"); +}, []); + + +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "Can React.memo prevent every re-render?" *"No. It only skips re-rendering when props remain unchanged. If the component's own state changes or new object/function references are passed as props, it will still re-render."* + +--- + +### 8. What are Higher Order Components (HOCs)? + +A Higher-Order Component (HOC) is a function that takes a component as input and returns a new enhanced component with additional functionality. + +HOCs allow developers to reuse component logic without modifying the original component. They were commonly used before React Hooks for features such as authentication, logging, permissions, and data fetching. + +A HOC does not modify the original component; instead, it wraps it. + +**Syntax** + +```javascript +const EnhancedComponent = higherOrderComponent(WrappedComponent); +``` + +**Example** + +```javascript +function withLogger(Component) { + return function EnhancedComponent(props) { + console.log("Component Rendered"); + + return ; + }; +} +``` + +**Usage** + +```javascript +const ProfileWithLogger = withLogger(Profile); +``` + +**Common Use Cases** + +- Authentication +- Authorization +- Logging +- Analytics +- Permissions +- Error handling + +**HOC Flow** + +```text +Component + β”‚ + β–Ό +Higher Order Component + β”‚ + β–Ό +Enhanced Component +``` + +**Difference between HOC and Custom Hook** + +| HOC | Custom Hook | +| --- | --- | +| Wraps a component | Reuses logic | +| Returns new component | Returns state and functions | +| Older React pattern | Modern React pattern | +| Used before Hooks | Preferred today | + +--- + +> πŸ’‘ **Interview Tip:** If asked "Are HOCs still used today?" *"HOCs are still found in legacy codebases and some libraries like React Redux's connect(), but for new applications, Custom Hooks are generally preferred because they provide cleaner and more reusable logic."* + +--- + +### 9. What are Custom Hooks? + +A Custom Hook is a JavaScript function whose name starts with `use` and that uses one or more React Hooks internally. + +Custom Hooks allow developers to extract and reuse stateful logic across multiple components without duplicating code. They improve code organization, readability, and maintainability. + +A Custom Hook does not render UI; it simply shares logic. + +**Example** + +```javascript +import { useState } from "react"; + +function useCounter() { + const [count, setCount] = useState(0); + + const increment = () => setCount(count + 1); + + return { + count, + increment, + }; +} +``` + +**Usage** + +```javascript +function App() { + const { count, increment } = useCounter(); + + return ( + <> +

{count}

+ + + + ); +} +``` + +**Common Use Cases** + +- Authentication +- API calls +- Form validation +- Theme management +- Local Storage +- Window resize +- Debouncing + +**Why use Custom Hooks?** + +- Code reuse +- Cleaner components +- Better separation of concerns +- Easier testing +- Less duplication + +--- + +> πŸ’‘ **Interview Tip:** If asked "When should you create a Custom Hook?" *"Whenever the same stateful logic is used in multiple components, I extract it into a Custom Hook instead of duplicating code."* + +--- + +### 10. What is Lazy Loading? + +Lazy Loading is a performance optimization technique where components or resources are loaded only when they are needed, instead of loading everything during the initial page load. + +React provides `React.lazy()` and `Suspense` to implement Lazy Loading. + +This reduces the application's initial bundle size, speeds up page loading, and improves user experience. + +**Without Lazy Loading** + +```text +App Loads + β”‚ + β–Ό +Load Every Component +``` + +Large JavaScript bundle. + +**With Lazy Loading** + +```text +App Loads + β”‚ + β–Ό +Load Home Page + β”‚ + β–Ό +User Opens Dashboard + β”‚ + β–Ό +Load Dashboard Component +``` + +Only necessary components are downloaded. + +**Example** + +```javascript +import React, { Suspense } from "react"; + +const Dashboard = React.lazy(() => import("./Dashboard")); + +function App() { + return ( + Loading...}> + + + ); +} +``` + +**Common Use Cases** + +- Large pages +- Dashboards +- Admin panels +- Heavy charts +- Route-based loading + +**Benefits** + +- Faster initial load +- Smaller bundle size +- Better performance +- Improved user experience + +--- + +> πŸ’‘ **Interview Tip:** If asked "What is the difference between Lazy Loading and Code Splitting?" *"Code Splitting divides the application into multiple JavaScript bundles, while Lazy Loading loads those bundles only when they are actually needed. Lazy Loading usually relies on Code Splitting to work efficiently."* + +--- + +Today 9:42 AM + +next 5 question + +--- + +### 11. What is Code Splitting? + +Code Splitting is a technique of dividing a large JavaScript bundle into smaller chunks that can be loaded independently when needed. Instead of downloading the entire application when the user first opens it, only the code required for the current page or feature is loaded. + +In React, Code Splitting is commonly implemented using dynamic `import()`, `React.lazy()`, and `Suspense`. It improves initial load performance, especially for large applications. + +**Example** + +```javascript +import { lazy, Suspense } from "react"; + +const Dashboard = lazy(() => import("./Dashboard")); + +function App() { + return ( + Loading...

}> + +
+ ); +} +``` + +Here, the Dashboard code is placed in a separate chunk and downloaded when it is needed. + +**Code Splitting vs Lazy Loading** + +| Code Splitting | Lazy Loading | +| --- | --- | +| Divides code into smaller chunks | Loads a chunk only when needed | +| Focuses on bundle structure | Focuses on loading timing | +| Uses dynamic imports | Commonly uses `React.lazy()` | +| Helps reduce initial bundle size | Helps avoid loading unused code | + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why is Code Splitting important?" *"Code Splitting reduces the amount of JavaScript that the browser needs to download initially. By loading only the code required for the current page or feature, it improves initial load time and application performance."* + +--- + +### 12. Explain React Router and Protected Routes. + +React Router is a routing library used to handle navigation between different views or pages in a React application without performing a full browser refresh. It maps URLs to React components and allows features such as nested routes, dynamic routes, navigation, and route parameters. + +A **Protected Route** is a route that can only be accessed when a particular condition is satisfied, such as the user being authenticated. If the user is not authenticated, the application redirects them to a login page. + +**Example** + +```javascript +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; + +function ProtectedRoute({ children }) { + const isLoggedIn = true; + + return isLoggedIn ? children : ; +} + +function App() { + return ( + + + } /> + + + + + } + /> + + + ); +} +``` + +**Common React Router Features** + +- Client-side navigation +- Dynamic routes +- Nested routes +- Route parameters +- Protected routes +- Programmatic navigation + +**Real-world Example** + +In an e-commerce application: + +```text +/login β†’ Public +/products β†’ Public +/cart β†’ Public +/checkout β†’ Protected +/profile β†’ Protected +/admin β†’ Protected + Authorization +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "How do you implement a protected route?" *"I check whether the user is authenticated before rendering the protected component. If the user is authenticated, I render the requested page; otherwise, I redirect them to the login page."* + +--- + +### 13. Difference between Client-Side Rendering (CSR) and Server-Side Rendering (SSR). + +Client-Side Rendering (CSR) means the browser initially receives a basic HTML document and JavaScript, and the React application generates the UI in the browser. Server-Side Rendering (SSR) means the server generates the initial HTML for the requested page and sends that HTML to the browser, after which JavaScript hydrates the page to make it interactive. + +CSR is simple and works well for highly interactive applications, while SSR can provide faster initial content and better SEO for pages where search engine visibility and first-load performance are important. + +**CSR** + +```text +Browser + ↓ +HTML + JavaScript + ↓ +React runs + ↓ +UI rendered +``` + +**Advantages** + +- Good for highly interactive applications +- Less server-side rendering work +- Smooth navigation after initial load + +**Disadvantages** + +- Initial page may take longer to display meaningful content +- SEO can be more challenging depending on implementation + +**SSR** + +```text +Browser + ↓ +Request + ↓ +Server + ↓ +HTML generated + ↓ +Browser displays HTML + ↓ +React hydrates +``` + +**Advantages** + +- Faster initial content +- Better SEO +- Good for content-heavy pages + +**Disadvantages** + +- More server-side work +- More complex architecture +- Requires hydration for interactivity + +**Example** + +Traditional React SPA: + +```text +React β†’ Browser β†’ Render +``` + +Frameworks such as Next.js can support: + +```text +Server β†’ HTML β†’ Browser β†’ Hydration β†’ Interactive React +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "Which one should you choose?" *"For highly interactive applications where SEO is less important, CSR can be a good choice. For public pages such as e-commerce products, blogs, and marketing pages where SEO and fast initial content matter, SSR can provide significant benefits."* + +--- + +### 14. How do you optimize React performance? + +React performance can be improved by reducing unnecessary renders, reducing the amount of JavaScript loaded initially, and avoiding expensive calculations during rendering. Common techniques include `React.memo`, `useMemo`, `useCallback`, Code Splitting, Lazy Loading, virtualization for large lists, proper state management, and using stable keys. + +However, these optimizations should not be applied blindly because memoization also has a cost. First identify the actual performance bottleneck and then apply the appropriate optimization. + +**Common Techniques** + +**1. React.memo** β€” Prevents unnecessary child re-renders when props haven't changed. + +```javascript +const User = React.memo(function User({ name }) { + return

{name}

; +}); +``` + +**2. useMemo** β€” Memoizes expensive calculations. + +```javascript +const result = useMemo(() => expensiveCalculation(data), [data]); +``` + +**3. useCallback** β€” Keeps a function reference stable. + +```javascript +const handleClick = useCallback(() => { + console.log("Clicked"); +}, []); +``` + +**4. Code Splitting** β€” Load JavaScript only when required. + +```javascript +const Dashboard = lazy(() => import("./Dashboard")); +``` + +**5. List Virtualization** β€” For thousands of items, render only the items currently visible instead of rendering the entire list. + +**Other Important Optimizations** + +- Keep state as local as possible. +- Avoid unnecessary global state. +- Use stable keys. +- Avoid creating unnecessary objects/functions in props. +- Optimize large images and assets. +- Use pagination or infinite scrolling for large datasets. +- Profile the application before optimizing. + +--- + +> πŸ’‘ **Interview Tip:** If asked "What would you do if a React application is slow?" *"First, I would identify the bottleneck using profiling tools instead of immediately adding memoization. Then I would optimize unnecessary re-renders, expensive calculations, large lists, bundle size, and network resources using techniques such as React.memo, memoization, Code Splitting, Lazy Loading, and list virtualization."* + +--- + +### 15. What is Optimistic UI? + +Optimistic UI is a technique where the application updates the user interface immediately before the server confirms that an operation succeeded. The application assumes the request will succeed, providing a faster and more responsive user experience. + +If the server request succeeds, the UI remains updated. If the request fails, the application should rollback the optimistic change and show an appropriate error message. + +**Example** + +Suppose a user likes a post. + +**Without Optimistic UI:** + +```text +Click Like + ↓ +API Request + ↓ +Server Response + ↓ +Update Like Button +``` + +The user may experience a delay. + +**With Optimistic UI:** + +```text +Click Like + ↓ +Immediately show "Liked" ❀️ + ↓ +Send API Request + ↓ +Success β†’ Keep UI +Failure β†’ Rollback UI +``` + +**Example** + +```javascript +async function handleLike() { + // Optimistically update UI + setLiked(true); + + try { + await likePost(); + } catch (error) { + // Rollback if request fails + setLiked(false); + } +} +``` + +**Common Use Cases** + +- Like/unlike buttons +- Follow/unfollow +- Adding items to cart +- Updating profile settings +- Sending messages +- Todo completion +- Comments + +**Why use Optimistic UI?** + +- Makes applications feel faster. +- Provides immediate feedback. +- Improves user experience. +- Reduces the perceived network delay. + +The important part is handling failure correctly, because the UI temporarily represents an assumption rather than confirmed server state. + +--- + +> πŸ’‘ **Interview Tip:** If asked "What happens if the API fails?" *"I roll back the optimistic UI change and show an appropriate error message or retry option. Optimistic UI should always have a failure-handling strategy so that the client doesn't remain inconsistent with the server."* + +--- + +## 🟒 Node.js & Express (18) + +### 1. Explain the Node.js architecture. + +Node.js is a JS runtime built on Chrome's V8 JS engine that allows to run JS outside the browser. Its architecture follows a single-threaded, event-driven, non-blocking I/O model, which makes it efficient for handling many concurrent requests. + +The main JS code runs on a single thread while asynchronous operations such as file system operations, networking, and some other tasks are handled by libuv and the operating system. When those operations complete their callbacks are processed by the Event Loop. + +**Node.js Architecture** + +```text +JavaScript Code + β”‚ + β–Ό + V8 Engine + β”‚ + β–Ό + Call Stack + β”‚ + β–Ό + Event Loop + β”‚ + β–Ό + libuv + / \ + β–Ό β–Ό +OS APIs Thread Pool +``` + +**Example** + +```javascript +const fs = require("fs"); + +console.log("Start"); + +fs.readFile("data.txt", "utf8", (err, data) => { + console.log(data); +}); + +console.log("End"); +``` + +**Output:** + +```text +Start +End +file contents +``` + +`fs.readFile()` is asynchronous, so Node.js doesn't block the main thread while waiting for the file operation to complete. + +**Main Components** + +- **V8 Engine** β†’ Executes JavaScript. +- **Call Stack** β†’ Executes synchronous JavaScript code. +- **Event Loop** β†’ Coordinates asynchronous operations. +- **libuv** β†’ Provides Node.js's asynchronous, non-blocking I/O mechanism. +- **Thread Pool** β†’ Handles certain operations such as file system operations, DNS, and cryptographic tasks. + +--- + +### 2. Explain the Node.js Event Loop. + +The Node.js Event Loop is the mechanism that allows Node.js to perform asynchronous operations without blocking the main JavaScript thread. Synchronous code runs on the Call Stack, while asynchronous operations are handled by Node.js, libuv, or the operating system. + +When an asynchronous operation completes, its callback is scheduled to run, and the Event Loop executes it when the Call Stack is available. This allows Node.js to handle many concurrent requests efficiently despite JavaScript execution being single-threaded. + +**Example** + +```javascript +console.log("Start"); + +setTimeout(() => { + console.log("Timer"); +}, 0); + +console.log("End"); +``` + +**Output:** + +```text +Start +End +Timer +``` + +The timer callback doesn't execute immediately because Node.js first finishes the synchronous code. + +**Event Loop Flow** + +```text +Call Stack + ↓ +Asynchronous Operation + ↓ +Callback Scheduled + ↓ +Event Loop + ↓ +Call Stack +``` + +**Node.js Event Loop has several phases, including:** + +- Timers +- Pending callbacks +- Poll +- Check +- Close callbacks + +For example, `setTimeout()` callbacks are handled during the Timers phase, while `setImmediate()` callbacks are handled during the Check phase. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Is Node.js single-threaded?" *"JavaScript execution in Node.js happens on a single main thread, but Node.js itself isn't completely single-threaded. libuv can use a thread pool for certain operations, and Node.js also provides Worker Threads for CPU-intensive JavaScript tasks."* + +--- + +### 3. What are Worker Threads? + +Worker Threads allow Node.js to execute JavaScript code in separate threads instead of the main JavaScript thread. They are mainly useful for CPU-intensive tasks that could otherwise block the Event Loop. + +Node.js is very efficient for I/O operations, but CPU-heavy tasks such as image processing, large calculations, or complex data processing can block the main thread. Worker Threads allow these operations to run separately while keeping the main Event Loop responsive. + +**Example β€” Main Thread** + +```javascript +const { Worker } = require("worker_threads"); + +const worker = new Worker("./worker.js"); + +worker.on("message", (result) => { + console.log("Result:", result); +}); +``` + +**Example β€” Worker** + +```javascript +const { parentPort } = require("worker_threads"); + +let result = 0; + +for (let i = 0; i < 1000000000; i++) { + result += i; +} + +parentPort.postMessage(result); +``` + +The expensive calculation runs in the Worker instead of blocking the main thread. + +**Common Use Cases** + +- Image processing +- Video processing +- Large calculations +- Data processing +- CPU-intensive algorithms + +**Worker Threads vs Event Loop** + +| Worker Threads | Event Loop | +| --- | --- | +| Runs JavaScript on separate threads | Runs JavaScript on the main thread | +| Handles CPU-intensive JavaScript | Handles asynchronous coordination | +| Good for CPU-heavy tasks | Good for I/O-heavy tasks | +| Prevents heavy computation from blocking the main thread | Keeps normal requests non-blocking | + +--- + +### 4. What are Streams? + +Streams are objects in Node.js that allow us to process data piece by piece instead of loading the entire data into memory at once. They are especially useful when working with large files, HTTP requests, video, and continuous data. + +Streams make applications more memory-efficient because data can be processed in smaller chunks. + +Node.js provides four main types of streams: **Readable**, **Writable**, **Duplex**, and **Transform**. + +**Example** + +```javascript +const fs = require("fs"); + +const stream = fs.createReadStream("large-file.txt"); + +stream.on("data", (chunk) => { + console.log("Received:", chunk.length); +}); + +stream.on("end", () => { + console.log("Finished reading"); +}); +``` + +Instead of loading the complete file into memory, Node.js reads it in smaller chunks. + +**Types of Streams** + +**Readable Stream** β€” Used to read data. + +```javascript +fs.createReadStream("file.txt"); +``` + +**Writable Stream** β€” Used to write data. + +```javascript +fs.createWriteStream("output.txt"); +``` + +**Duplex Stream** β€” Can both read and write data. (Readable + Writable) + +**Transform Stream** β€” Can modify data while it is being read or written. (Input β†’ Transform β†’ Output) + +--- + +### 5. What are Buffers? + +A Buffer is a Node.js object used to store and manipulate raw binary data in memory. JavaScript normally works with strings and objects, but Node.js often needs to work with binary data such as images, files, network packets, and streams. + +A Buffer represents binary data as a sequence of bytes. Streams commonly use Buffers to process data in chunks. + +**Example** + +```javascript +const buffer = Buffer.from("Hello"); + +console.log(buffer); +console.log(buffer.toString()); +``` + +**Output:** + +```text + +Hello +``` + +The Buffer stores `"Hello"` as its underlying byte representation. + +**Streams often provide data as Buffer chunks:** + +```text +Large File + ↓ +Stream + ↓ +Buffer Chunks + ↓ +Process Data +``` + +This allows Node.js to process large files without loading the entire file into memory. + +--- + +### 6. What is the purpose of package.json? + +`package.json` is the configuration and metadata file of a Node.js project. It contains important information such as the project name, version, dependencies, scripts, entry point, and project configuration. + +It helps package managers like npm, pnpm, and Yarn understand how to install and manage the project's dependencies. It also allows developers to define reusable commands such as `npm run dev`, `npm test`, and `npm run build`. + +**Common Fields** + +- `name` β†’ Project name. +- `version` β†’ Project version. +- `scripts` β†’ Commands used to run project tasks. +- `dependencies` β†’ Packages required in production. +- `devDependencies` β†’ Packages required during development. +- `main` β†’ Entry point of the package. +- `type` β†’ Determines the module system, such as `"module"` for ES Modules. + +--- + +### 7. What is Middleware in Express? + +Middleware is a function that runs during the request-response cycle and has access to the request object (`req`), response object (`res`), and the `next()` function. + +Middleware can perform tasks such as authentication, logging, validation, parsing request data, modifying requests or responses, and error handling. Multiple middleware functions can be executed sequentially before the final route handler sends a response. + +**Example** + +```javascript +const express = require("express"); + +const app = express(); + +function logger(req, res, next) { + console.log(`${req.method} ${req.url}`); + next(); +} + +app.use(logger); + +app.get("/users", (req, res) => { + res.json({ message: "Users" }); +}); +``` + +When `/users` is requested, the `logger` middleware runs first and then calls `next()` to continue to the route handler. + +**Middleware Flow** + +```text +Request + ↓ +Middleware 1 + ↓ +Middleware 2 + ↓ +Authentication + ↓ +Validation + ↓ +Route Handler + ↓ +Response +``` + +**Common Types of Middleware** + +- Application-level middleware β†’ `app.use()` +- Router-level middleware β†’ `router.use()` +- Built-in middleware β†’ `express.json()` +- Third-party middleware β†’ `cors`, `morgan` +- Error-handling middleware β†’ `(err, req, res, next)` + +--- + +### 8. What is the purpose of next()? + +`next()` is a function provided by Express that tells the current middleware to pass control to the next middleware or route handler in the request-response chain. + +If middleware performs its task but doesn't send a response, it should normally call `next()`. If it doesn't call `next()` or send a response, the request can remain hanging. + +**Example** + +```javascript +function auth(req, res, next) { + const token = req.headers.authorization; + + if (!token) { + return res.status(401).json({ + message: "Unauthorized" + }); + } + + req.user = { id: "123" }; + + next(); +} + +app.get("/profile", auth, (req, res) => { + res.json(req.user); +}); +``` + +If the token exists, `next()` allows the request to continue to `/profile`. + +**next() in Different Situations** + +- Continue to next middleware: `next();` +- Pass an error to error-handling middleware: `next(error);` +- Stop the request: `return res.status(401).json({ message: "Unauthorized" });` + +**Middleware Flow** + +```text +Request + ↓ +auth() + ↓ +next() + ↓ +validation() + ↓ +next() + ↓ +controller() + ↓ +Response +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "What happens if you forget next()?" *"If the middleware doesn't send a response and doesn't call next(), Express cannot continue to the next middleware or route handler, so the request may remain pending until it eventually times out."* + +--- + +### 9. How does Error Handling Middleware work? + +Error-handling middleware is special Express middleware used to catch and handle errors that occur during the request-response lifecycle. It has four parameters: `err`, `req`, `res`, and `next`. + +When an error is passed to `next(error)`, Express skips normal middleware and looks for the next error-handling middleware. Centralizing errors in one place keeps route handlers clean and allows the API to return consistent error responses. + +**Example** + +```javascript +app.get("/users", (req, res, next) => { + try { + throw new Error("Database failed"); + } catch (error) { + next(error); + } +}); +``` + +**Error-handling middleware:** + +```javascript +app.use((err, req, res, next) => { + console.error(err); + + res.status(500).json({ + success: false, + message: "Internal Server Error" + }); +}); +``` + +--- + +### 10. Difference between app.use() and app.get(). + +`app.use()` is mainly used to register middleware that can run for multiple HTTP methods and paths, while `app.get()` specifically defines a GET route for a particular path. + +`app.use()` can execute before routes and is commonly used for middleware such as authentication, logging, JSON parsing, and routers. `app.get()` is used when we want to handle a GET request and normally sends the final response. + +**Example** + +```javascript +app.use(express.json()); + +app.use("/api", authMiddleware); + +app.get("/users", (req, res) => { + res.json({ message: "Users list" }); +}); +``` + +Here, `express.json()` and `authMiddleware` are middleware, while `/users` is a specific GET route. + +**Difference** + +| `app.use()` | `app.get()` | +| --- | --- | +| Registers middleware | Registers a GET route | +| Can work with multiple HTTP methods | Only handles GET requests | +| Commonly used for middleware | Used to handle GET requests | +| Can mount routers | Defines a specific endpoint | +| Usually calls `next()` | Usually sends a response | + +--- + +### 11. How does Routing work in Express? + +Routing in Express determines how an application responds to a specific HTTP method and URL path. We define routes using methods such as `app.get()`, `app.post()`, `app.put()`, `app.patch()`, and `app.delete()`. + +When a request arrives, Express checks the registered routes and middleware in order. If the HTTP method and path match, Express executes the corresponding route handler. + +**Example** + +```javascript +const express = require("express"); + +const app = express(); + +app.get("/users", (req, res) => { + res.json({ message: "Get users" }); +}); + +app.post("/users", (req, res) => { + res.json({ message: "Create user" }); +}); + +app.delete("/users/:id", (req, res) => { + res.json({ message: "Delete user" }); +}); +``` + +```text +GET /users β†’ Get users +POST /users β†’ Create user +DELETE /users/:id β†’ Delete user +``` + +--- + +### 12. Explain the Request-Response Lifecycle in Express. + +The Request-Response lifecycle describes what happens from the moment a client sends an HTTP request until Express sends a response back. + +The request first reaches the Express server and passes through the registered middleware in order. Middleware can modify the request, authenticate the user, validate data, or terminate the request. If everything is valid, the request reaches the route handler or controller, which performs the required operation and sends the response back to the client. + +**Lifecycle** + +```text +Client + ↓ +HTTP Request + ↓ +Express Server + ↓ +Middleware + ↓ +Authentication + ↓ +Validation + ↓ +Route + ↓ +Controller + ↓ +Service / Database + ↓ +Response + ↓ +Client +``` + +**Important Steps** + +- Client sends request. +- Express receives the request. +- Middleware processes the request. +- Router finds matching route. +- Controller executes business logic. +- Database or external service may be called. +- Response is sent to the client. +- Error middleware handles failures when necessary. + +--- + +### 13. How should you structure an Express project? + +An Express project should be structured by separating routes, controllers, services, models, middleware, configuration, and utilities instead of putting everything into one large file. + +This separation follows the **Separation of Concerns** principle. Routes handle endpoint definitions, controllers handle HTTP-level logic, services contain business logic, models handle database interaction, and middleware handles cross-cutting concerns such as authentication and error handling. + +**Example Structure** + +```text +src/ +β”œβ”€β”€ controllers/ +β”‚ └── user.controller.js +β”‚ +β”œβ”€β”€ routes/ +β”‚ └── user.routes.js +β”‚ +β”œβ”€β”€ services/ +β”‚ └── user.service.js +β”‚ +β”œβ”€β”€ models/ +β”‚ └── user.model.js +β”‚ +β”œβ”€β”€ middleware/ +β”‚ β”œβ”€β”€ auth.middleware.js +β”‚ └── error.middleware.js +β”‚ +β”œβ”€β”€ config/ +β”‚ └── database.js +β”‚ +β”œβ”€β”€ utils/ +β”‚ └── logger.js +β”‚ +β”œβ”€β”€ app.js +└── server.js +``` + +**Request Flow** + +```text +Route + ↓ +Controller + ↓ +Service + ↓ +Model / Database +``` + +--- + +### 14. Why are Environment Variables important? + +Environment variables are external configuration values provided to an application at runtime instead of hardcoding them directly into source code. They are commonly used for sensitive or environment-specific values such as database URLs, API keys, JWT secrets, and server ports. + +They allow the same codebase to run in different environments such as development, testing, staging, and production without changing the source code. + +--- + +### 15. How do you implement Logging? + +Logging means recording important application events, requests, errors, and system information so developers can monitor, debug, and troubleshoot an application. + +In Express, we can use middleware such as Morgan for HTTP request logging and a dedicated logger such as Pino or Winston for structured application logs. In production, logs should include useful information such as timestamps, HTTP methods, status codes, request IDs, and error details without exposing sensitive information. + +**Simple Example** + +```javascript +const express = require("express"); +const morgan = require("morgan"); + +const app = express(); + +app.use(morgan("combined")); +``` + +Now requests can produce logs such as: + +```text +GET /api/users 200 +POST /api/login 401 +``` + +**Custom Logging** + +```javascript +function logger(req, res, next) { + console.log( + `${new Date().toISOString()} ${req.method} ${req.url}` + ); + + next(); +} + +app.use(logger); +``` + +**What should you log?** + +- Request method +- URL +- Status code +- Response time +- Errors +- Request ID +- Important application events + +**Avoid logging sensitive information such as:** + +- Passwords +- JWT secrets +- API keys +- Credit card information +- Private user data + +--- + +### 16. What are REST API best practices? + +REST API best practices are conventions that make APIs consistent, predictable, secure, and easy to maintain. A good REST API uses meaningful resource-based URLs, appropriate HTTP methods and status codes, consistent response formats, validation, authentication, pagination, and proper error handling. + +For example, instead of using action-based URLs like `/getUsers`, we use resource-based endpoints such as `GET /users`. + +**Example** + +```text +GET /api/users β†’ Get users +GET /api/users/123 β†’ Get one user +POST /api/users β†’ Create user +PATCH /api/users/123 β†’ Update user +DELETE /api/users/123 β†’ Delete user +``` + +**Important Best Practices** + +- Use nouns for resources, not verbs. +- Use HTTP methods correctly. +- Return meaningful HTTP status codes. +- Validate incoming data. +- Use consistent response formats. +- Implement authentication and authorization. +- Handle errors centrally. +- Use pagination for large collections. +- Version APIs when necessary. +- Don't expose sensitive information. +- Use HTTPS in production. + +**Common HTTP Status Codes** + +- `200` β†’ Successful request +- `201` β†’ Resource created +- `400` β†’ Bad request +- `401` β†’ Not authenticated +- `403` β†’ Not authorized +- `404` β†’ Resource not found +- `409` β†’ Conflict +- `500` β†’ Internal server error + +--- + +### 17. How do you implement File Uploads? + +File uploads in Express are commonly implemented using `multipart/form-data` and middleware such as Multer. Multer processes the incoming multipart request and makes the uploaded file available through `req.file` or multiple files through `req.files`. + +For production applications, I would also validate the file type and size and usually store large files in object storage such as Amazon S3 or Cloudinary instead of keeping them permanently on the application server. + +```javascript +const express = require("express"); +const multer = require("multer"); + +const app = express(); + +const upload = multer({ + dest: "uploads/" +}); + +app.post("/upload", upload.single("image"), (req, res) => { + console.log(req.file); + + res.json({ + message: "File uploaded successfully" + }); +}); +``` + +--- + +### 18. How should APIs be Versioned? + +API versioning allows us to introduce breaking changes to an API without immediately breaking existing clients. Instead of changing the existing contract, we create a new version such as `/api/v2/users` while keeping `/api/v1/users` available during the migration period. + +Versioning is especially important when APIs are consumed by mobile applications, frontend applications, or external clients that may not be updated at the same time as the backend. + +**Example** + +```text +/api/v1/users +/api/v2/users +``` + +--- + +## πŸƒ MongoDB (15) + +### 1. SQL vs NoSQL. + +SQL databases are relational databases that store data in structured tables with predefined schemas and use SQL for querying. NoSQL databases use flexible data models such as documents, key-value pairs, graphs, or wide-column structures and are designed for different scalability and data-access requirements. + +SQL databases are generally a strong choice when you need complex relationships, joins, and strict transactional consistency. MongoDB is a NoSQL document database that is useful when the data structure changes frequently, documents naturally represent the data, or horizontal scaling is important. + +**Difference** + +| SQL | NoSQL | +| --- | --- | +| Relational | Non-relational | +| Tables and rows | Documents, key-value, etc. | +| Usually fixed schema | Flexible schema | +| Uses SQL | Database-specific query APIs/languages | +| Strong support for joins | Often favors embedding or application-side relationships | +| Traditionally scales vertically, though modern systems can scale horizontally too | Often designed with horizontal scaling in mind | +| Examples: PostgreSQL, MySQL | Examples: MongoDB, Redis, Cassandra | + +--- + +### 2. What is BSON? + +BSON stands for **Binary JSON** and is the format MongoDB uses to store and transmit documents. It extends JSON by supporting additional data types such as `ObjectId`, `Date`, `Decimal128`, and binary data. + +Although MongoDB documents look like JSON when we work with them, MongoDB internally stores them as BSON. BSON also stores type information, which allows MongoDB to distinguish between values such as a string, date, integer, and ObjectId. + +**BSON vs JSON** + +| JSON | BSON | +| --- | --- | +| Text-based format | Binary format | +| Limited data types | Supports more data types | +| Human-readable | Designed for machine storage/transmission | +| Common web data format | MongoDB's storage/document format | + +--- + +### 3. What is the difference between Collections and Documents? + +A **Document** is an individual record in MongoDB, represented as a BSON object containing fields and values. A **Collection** is a group of related documents and is roughly comparable to a table in a relational database. + +Unlike SQL tables, MongoDB collections don't require every document to have exactly the same fields, which provides a flexible schema. + +--- + +### 4. How should you design MongoDB Schemas? + +MongoDB schema design should start with how the application accesses the data, rather than simply converting an SQL schema into collections. The main decision is whether related data should be embedded inside a document or stored separately and referenced. + +Embedding is useful when related data is commonly read together and has a manageable size. Referencing is better when related data is large, independently accessed, shared by many documents, or changes frequently. + +**Embedding vs Referencing** + +| Embedding | Referencing | +| --- | --- | +| Related data stored together | Related data stored separately | +| Fewer queries | May require additional queries | +| Good for one-to-few relationships | Good for large/independent relationships | +| Fast when data is read together | Better when related data is shared | +| Can cause large documents | Avoids excessive document growth | + +--- + +### 5. What is Mongoose? + +Mongoose is an **ODM (Object Data Modeling)** library for MongoDB and Node.js. It provides a structured way to define schemas and models and makes it easier to interact with MongoDB from a Node.js application. + +Mongoose provides features such as schemas, validation, middleware, type casting, model methods, and population. MongoDB itself is schema-flexible, while Mongoose allows the application to enforce a consistent structure at the application layer. + +--- + +### 6. Explain CRUD Operations in MongoDB. + +CRUD stands for **Create, Read, Update, and Delete**, which are the four basic operations used to manage data in MongoDB. MongoDB provides methods such as `insertOne()`, `find()`, `updateOne()`, and `deleteOne()` for these operations. + +In a Node.js application using Mongoose, the same operations can be performed through models such as `create()`, `find()`, `findByIdAndUpdate()`, and `findByIdAndDelete()`. + +**Create** β€” Used to add a new document. + +```javascript +const user = await User.create({ + name: "Shubham", + age: 22 +}); +``` + +**Read** β€” Used to retrieve documents. + +```javascript +const users = await User.find(); + +const user = await User.findById(userId); +``` + +**Update** β€” Used to modify an existing document. + +```javascript +await User.findByIdAndUpdate( + userId, + { age: 23 }, + { new: true } +); +``` + +`new: true` returns the updated document. + +**Delete** β€” Used to remove a document. + +```javascript +await User.findByIdAndDelete(userId); +``` + +**CRUD Flow** + +```text +Create β†’ Insert data +Read β†’ Retrieve data +Update β†’ Modify data +Delete β†’ Remove data +``` + +--- + +### 7. What is Indexing in MongoDB? + +Indexing is a technique that improves query performance by creating a data structure that MongoDB can use to find documents without scanning the entire collection. + +Without an appropriate index, MongoDB may perform a collection scan, checking many or all documents. With an index, MongoDB can locate matching documents more efficiently, but indexes also consume memory and make write operations slightly more expensive because the indexes must be maintained. + +**Common Types of Indexes** + +**Single Field Index** + +```javascript +db.users.createIndex({ + email: 1 +}); +``` + +**Compound Index** + +```javascript +db.users.createIndex({ + age: 1, + name: 1 +}); +``` + +**Unique Index** + +```javascript +db.users.createIndex( + { email: 1 }, + { unique: true } +); +``` + +This prevents duplicate email values. + +**Important Point** + +Indexes improve reads but have a cost. + +```text +Without Index +Query β†’ Scan many documents β†’ Result + +With Index +Query β†’ Index β†’ Matching documents β†’ Result +``` + +Indexes consume storage/memory and can increase the cost of inserts, updates, and deletes. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Should we create an index on every field?" *"No. Indexes should be created based on actual query and sort patterns. Too many indexes consume memory and increase write overhead, so I would analyze slow queries and create indexes that provide measurable benefits."* + +--- + +### 8. Explain the Aggregation Pipeline. + +The Aggregation Pipeline is a framework in MongoDB used to process documents through a sequence of stages and produce transformed or calculated results. + +Each stage receives documents from the previous stage, performs an operation such as filtering, grouping, sorting, or joining, and passes the resulting documents to the next stage. + +**Example** + +Suppose we want to find the average salary for each department: + +```javascript +db.employees.aggregate([ + { + $group: { + _id: "$department", + averageSalary: { + $avg: "$salary" + } + } + } +]); +``` + +**Common Aggregation Stages** + +**$match** β€” Filters documents. + +```javascript +{ + $match: { + age: { $gte: 18 } + } +} +``` + +**$group** β€” Groups documents and performs calculations. + +```javascript +{ + $group: { + _id: "$department", + total: { $sum: 1 } + } +} +``` + +**$sort** β€” Sorts documents. + +```javascript +{ + $sort: { + salary: -1 + } +} +``` + +**$project** β€” Selects or transforms fields. + +```javascript +{ + $project: { + name: 1, + salary: 1 + } +} +``` + +**$lookup** β€” Performs a join-like operation with another collection. + +```javascript +{ + $lookup: { + from: "orders", + localField: "_id", + foreignField: "userId", + as: "orders" + } +} +``` + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why use aggregation instead of normal find()?" *"`find()` is mainly used to retrieve matching documents, while the Aggregation Pipeline can perform complex data processing such as grouping, calculations, transformations, sorting, and lookup operations. I use aggregation when the application needs processed or analytical results rather than simply retrieving documents."* + +--- + +### 9. What is populate() in Mongoose? + +`populate()` is a Mongoose feature used to replace a referenced document's ID with the corresponding document data from another collection. + +MongoDB itself doesn't provide a traditional relational foreign-key mechanism, so Mongoose's `populate()` provides a convenient way to retrieve referenced documents when relationships are modeled using ObjectIds. + +**Important Point** + +`populate()` is convenient, but it should not be used blindly on large or deeply nested relationships because it can increase database work and response size. + +--- + +### 10. What are Transactions in MongoDB? + +Transactions allow multiple database operations to be executed as a single atomic unit. This means either all operations succeed and are committed, or if an error occurs, the transaction can be rolled back so that partial changes are not left in the database. + +MongoDB provides multi-document transactions, which are useful when multiple documents or collections must be updated consistently. Transactions should be used only when necessary because they add overhead compared with normal single-document operations. + +**Example** + +```javascript +const session = await mongoose.startSession(); + +try { + session.startTransaction(); + + await User.updateOne( + { _id: userId }, + { $inc: { balance: -100 } }, + { session } + ); + + await Account.updateOne( + { _id: accountId }, + { $inc: { balance: 100 } }, + { session } + ); + + await session.commitTransaction(); + +} catch (error) { + await session.abortTransaction(); + throw error; + +} finally { + session.endSession(); +} +``` + +Here, both balance updates are treated as one transaction. + +**Transaction Flow** + +```text +Start Transaction + ↓ +Operation 1 + ↓ +Operation 2 + ↓ +All Successful? + ↙ β†˜ + Yes No + ↓ ↓ +Commit Rollback +``` + +**ACID Properties** + +MongoDB transactions support the important ACID properties: + +- **Atomicity** β†’ All operations succeed or none are committed. +- **Consistency** β†’ Data remains valid according to the transaction's rules. +- **Isolation** β†’ Concurrent operations are handled according to transaction isolation semantics. +- **Durability** β†’ Committed changes persist according to the configured durability/write concern. + +--- + +### 11. Explain Replication and Replica Sets. + +Replication in MongoDB means maintaining multiple copies of data across different MongoDB servers. A group of MongoDB servers participating in replication is called a **Replica Set**. + +A Replica Set typically has one **Primary** node that accepts writes and one or more **Secondary** nodes that replicate data from the Primary. If the Primary becomes unavailable, eligible Secondaries can participate in an election and one can become the new Primary, providing high availability. + +**Replica Set** + +```text + Application + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Primary β”‚ + β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ + β”‚ + Replication + β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” + β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚Secondary β”‚ β”‚Secondary β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**How it works** + +The Primary handles writes and replicates changes to the Secondaries. Secondaries can also be configured to serve certain read workloads, although applications should carefully choose read preferences based on consistency requirements. + +**Primary vs Secondary** + +| Primary | Secondary | +| --- | --- | +| Handles normal writes | Replicates data | +| Default read target | Can serve reads depending on read preference | +| One primary at a time in a replica set | One or more secondaries | + +--- + +### 12. What is Sharding? + +Sharding is MongoDB's technique for distributing data across multiple servers, called **shards**, to horizontally scale storage and workload. + +Instead of storing an entire large collection on one server, MongoDB divides the data into smaller portions based on a shard key and distributes those portions across multiple shards. A sharded cluster can therefore handle datasets and workloads that exceed the practical capacity of a single server. + +**Example** + +```text + Application + β”‚ + β–Ό + mongos Router + / | \ + / | \ + β–Ό β–Ό β–Ό + Shard 1 Shard 2 Shard 3 +``` + +The `mongos` router directs operations to the appropriate shard or shards. + +**Shard Key** + +A shard key is a field or combination of fields MongoDB uses to distribute documents across shards. + +**Example:** + +```javascript +{ + userId: 12345, + name: "Shubham", + city: "Delhi" +} +``` + +A field such as `userId` could potentially be part of a shard key, depending on the application's query and distribution patterns. + +**Why Sharding?** + +- Very large datasets +- High throughput requirements +- Horizontal scaling +- Distributing storage across multiple machines +- Distributing read/write workload + +**Sharding vs Replication** + +| Sharding | Replication | +| --- | --- | +| Distributes data | Copies data | +| Used for horizontal scaling | Used mainly for high availability | +| Multiple shards contain different data | Replica members contain replicated data | +| Increases storage/workload capacity | Provides redundancy and failover | + +A production MongoDB deployment can use both sharding and replication. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why is choosing a shard key important?" *"The shard key determines how data is distributed across the cluster and strongly affects query routing and workload balance. A poor shard key can create uneven distribution or hotspots, while a well-designed key helps distribute data and traffic effectively."* + +--- + +### 13. Explain the CAP Theorem. + +The CAP Theorem states that a distributed system cannot simultaneously guarantee all three of these properties during a network partition: **Consistency**, **Availability**, and **Partition Tolerance**. + +- **Consistency** means every read receives the appropriate latest value according to the system's consistency guarantees. +- **Availability** means every request receives a response, even if it may not reflect the latest state. +- **Partition Tolerance** means the system continues operating despite communication failures between nodes. + +In real distributed systems, Partition Tolerance is generally required, because network failures can happen. The practical trade-off is therefore about how the system behaves regarding consistency and availability during a partition. (watch yt video for more understanding ) + +**MongoDB and CAP** + +MongoDB is designed to provide partition tolerance and offers configurable consistency/availability behavior through features such as replica sets, read preferences, write concerns, and read concerns. + +It's better in an interview to avoid simply saying "MongoDB is CP" without qualification, because MongoDB's actual behavior depends on the deployment configuration and the consistency guarantees being discussed. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Explain CAP in simple terms." *"CAP says that when a distributed system experiences a network partition, it cannot guarantee both perfect consistency and complete availability at the same time. Partition tolerance is essential in distributed systems, so the practical decision is how the system should behave during that partitionβ€”whether it should favor stronger consistency or continued availability."* + +--- + +### 14. How do you optimize MongoDB queries? + +MongoDB queries can be optimized by using the right indexes, returning only the required fields, limiting the number of documents processed, and designing queries according to actual application access patterns. I would first identify slow queries using tools such as `explain()` and MongoDB monitoring, then optimize based on the execution plan rather than adding indexes blindly. + +Indexes are especially important because they can prevent MongoDB from scanning the entire collection. However, too many indexes increase storage usage and add overhead to insert, update, and delete operations. + +**Example** + +```text +Without a suitable index: + +db.users.find({ + email: "shubham@example.com" +}); + +Create an index: + +db.users.createIndex({ + email: 1 +}); +``` + +Now MongoDB can efficiently use the index for suitable queries on `email`. + +**Use explain()** + +```javascript +db.users + .find({ email: "shubham@example.com" }) + .explain("executionStats"); +``` + +This helps inspect how MongoDB executed the query and whether an index was used. + +**Other Optimization Techniques** + +- Create indexes based on real query patterns. +- Use compound indexes for common multi-field queries. +- Return only required fields using projections. +- Use pagination for large datasets. +- Avoid unbounded result sets. +- Avoid unnecessary `$lookup` and expensive aggregation stages. +- Use appropriate schema design and embedding/reference decisions. +- Monitor slow queries. +- Use `lean()` with Mongoose when you only need plain JavaScript objects. + +--- + +> πŸ’‘ **Interview Tip:** If asked "How would you debug a slow MongoDB query?" *"First, I would reproduce and inspect the query using `explain('executionStats')` and check whether MongoDB is performing a collection scan or using an appropriate index. Then I would optimize the index, projection, query shape, pagination, or schema design based on the actual bottleneck rather than adding indexes blindly."* + +--- + +### 15. When would you use Redis instead of MongoDB? + +Redis and MongoDB solve different problems. MongoDB is primarily a persistent document database used as a system of record, while Redis is an in-memory data store commonly used for caching, sessions, counters, queues, rate limiting, and other low-latency workloads. + +I would use MongoDB when I need durable, queryable application data such as users, products, orders, or posts. I would use Redis when I need extremely fast temporary or frequently accessed data, often alongside MongoDB rather than replacing it. + +**Example** + +Suppose an application stores user information: + +```text +MongoDB + ↓ +User Profile +Orders +Posts +Products +``` + +For frequently requested data: + +```text +Request + ↓ +Redis Cache + ↓ +Cache Hit β†’ Return quickly + β”‚ + └── Cache Miss + ↓ + MongoDB + ↓ + Store in Redis +``` + +**Common Redis Use Cases** + +- Caching +- Session storage +- Rate limiting +- Counters +- Leaderboards +- Queues +- Pub/Sub +- Temporary data + +**Example: Cache** + +```javascript +const cachedUser = await redis.get(`user:${userId}`); + +if (cachedUser) { + return JSON.parse(cachedUser); +} + +const user = await User.findById(userId); + +await redis.set( + `user:${userId}`, + JSON.stringify(user), + { EX: 60 } +); +``` + +Here, Redis reduces repeated database queries for frequently requested user data. + +**Redis vs MongoDB** + +| Redis | MongoDB | +| --- | --- | +| Primarily in-memory | Primarily persistent document database | +| Extremely low latency | General-purpose application database | +| Key-value and other data structures | Document-oriented | +| Great for caching | Great for persistent application data | +| Commonly used for temporary/fast-access data | Commonly used as system of record | + +Usually, no. + +**A common architecture is:** + +```text +Client + ↓ +Node.js API + ↓ +Redis ──────→ Fast Cache + β”‚ + ↓ +MongoDB ────→ Persistent Data +``` + +Redis handles fast-access data while MongoDB remains the source of truth for persistent application data. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Why would you add Redis to a MongoDB application?" *"I would add Redis when I need to reduce database load or provide very low-latency access to frequently requested or temporary data. For example, I could cache popular API responses in Redis while keeping MongoDB as the persistent source of truth."* + +--- + +## πŸ” Authentication & Security (10) + +### 1. How does JWT Authentication work? + +JWT (JSON Web Token) authentication is a stateless authentication mechanism where the server generates a signed token after successfully verifying the user's credentials. The client sends this token with subsequent requests, and the server verifies the token to identify the user without needing to store the authentication session on the server. + +A JWT usually contains three parts: **Header**, **Payload**, and **Signature**. The payload can contain information such as the user's ID and role, but sensitive information should not be stored there because a normal JWT is encoded, not encrypted. + +**Authentication Flow** + +```text +Login + ↓ +Email + Password + ↓ +Server verifies credentials + ↓ +Generate JWT + ↓ +Client stores JWT + ↓ +Client sends JWT with requests + ↓ +Server verifies JWT + ↓ +Allow / Reject Request +``` + +**Example** + +```javascript +const token = jwt.sign( + { userId: user._id }, + process.env.JWT_SECRET, + { expiresIn: "15m" } +); +``` + +**Server verifies:** + +```javascript +const decoded = jwt.verify( + token, + process.env.JWT_SECRET +); +``` + +--- + +### 2. How do Refresh Tokens work? + +A Refresh Token is a long-lived credential used to obtain a new short-lived Access Token after the access token expires. This allows users to stay logged in without sending their password again while keeping the access token's lifetime short. + +A common architecture uses a short-lived access token for API requests and a longer-lived refresh token stored more securely, often in an HttpOnly, Secure cookie. + +**Authentication Flow** + +```text +Login + ↓ +Access Token + Refresh Token + ↓ +Access Token β†’ API Requests + ↓ +Access Token Expires + ↓ +Refresh Token β†’ /refresh + ↓ +New Access Token + ↓ +Continue Using API +``` + +--- + +### 3. Cookies vs LocalStorage. + +**Cookies** and **localStorage** can both store data in the browser, but they have different security and request-handling characteristics. Cookies can be automatically sent with HTTP requests to their matching domain, while localStorage is accessible only through JavaScript and is not automatically included in requests. + +For authentication, an HttpOnly, Secure cookie is generally preferred for sensitive session or refresh-token credentials because JavaScript cannot directly read an HttpOnly cookie. + +**Difference** + +| Cookies | localStorage | +| --- | --- | +| Can be HttpOnly | Always accessible to JavaScript | +| Can be Secure | No HttpOnly protection | +| Automatically sent with matching requests | Must be manually added to requests | +| Has expiration controls | Persists until removed or cleared | +| Can use SameSite protection | No SameSite cookie attribute | +| Better suited for sensitive session credentials | Useful for non-sensitive client-side data | + +**Cookie Example** + +```javascript +res.cookie("refreshToken", token, { + httpOnly: true, + secure: true, + sameSite: "strict" +}); +``` + +The browser manages the cookie and JavaScript cannot access an HttpOnly cookie. + +**localStorage Example** + +```javascript +localStorage.setItem("theme", "dark"); + +const theme = localStorage.getItem("theme"); +``` + +--- + +### 4. Why use bcrypt? + +bcrypt is a password-hashing algorithm designed specifically for securely storing passwords. Instead of storing the user's actual password, we store a one-way hash and compare a newly generated hash against the stored hash during login. + +bcrypt intentionally performs computationally expensive work and uses a salt, making brute-force and rainbow-table attacks more difficult. Passwords should never be stored as plaintext or using a fast general-purpose hash such as plain SHA-256. + +**Example** + +```javascript +const bcrypt = require("bcrypt"); + +const hashedPassword = await bcrypt.hash( + password, + 12 +); +``` + +During login: + +```javascript +const isValid = await bcrypt.compare( + password, + hashedPassword +); +``` + +If `isValid` is true, the password is correct. + +--- + +### 5. Authentication vs Authorization. + +**Authentication** answers "Who are you?", while **Authorization** answers "What are you allowed to do?" Authentication verifies the user's identity, usually through credentials, sessions, or tokens, while authorization checks whether that authenticated user has permission to perform a specific action or access a resource. + +**Example** + +Suppose a user logs into an admin dashboard: + +```text +Login + ↓ +Authentication + ↓ +"Is this user really Shubham?" + ↓ +Yes + ↓ +Authorization + ↓ +"Is Shubham an Admin?" + ↓ +Yes + ↓ +Allow Admin Action +``` + +**Authentication** + +Examples: + +- Email + password +- JWT +- Session +- OAuth +- MFA + +Example: + +```javascript +if (!user) { + return res.status(401).json({ + message: "Not authenticated" + }); +} +``` + +**Authorization** + +Example: + +```javascript +if (user.role !== "admin") { + return res.status(403).json({ + message: "Forbidden" + }); +} +``` + +--- + +### 6. What is Role-Based Access Control (RBAC)? + +RBAC (Role-Based Access Control) is an authorization system where permissions are assigned to roles, and users are assigned those roles. Instead of checking permissions separately for every user, the application checks the user's role and determines what actions that role is allowed to perform. + +For example, an application might have `admin`, `editor`, and `viewer` roles. An admin can manage users, an editor can create and update content, and a viewer can only read content. + +**Example** + +```javascript +const permissions = { + admin: ["read", "create", "update", "delete"], + editor: ["read", "create", "update"], + viewer: ["read"] +}; +``` + +Middleware can then check the user's role: + +```javascript +const authorize = (roles) => { + return (req, res, next) => { + if (!roles.includes(req.user.role)) { + return res.status(403).json({ + message: "Access denied" + }); + } + + next(); + }; +}; +``` + +**Usage:** + +```javascript +app.delete( + "/users/:id", + authenticate, + authorize(["admin"]), + deleteUser +); +``` + +**RBAC Flow** + +```text +User + ↓ +Authentication + ↓ +User Role + ↓ +Permission Check + ↓ +Allow / Deny +``` + +**Benefits** + +- Centralized authorization. +- Easier permission management. +- Avoids hardcoding permissions for individual users. +- Works well for admin dashboards and enterprise applications. +- Makes authorization logic easier to maintain. + +--- + +### 7. What is CORS? + +CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls whether a web page from one origin can make requests to a server on a different origin. An origin is determined by the combination of protocol, host, and port, so `http://localhost:3000` and `http://localhost:5000` are different origins. + +The server uses HTTP response headers to tell the browser which origins, methods, and headers are allowed. + +**Example** + +Suppose: + +```text +Frontend β†’ http://localhost:3000 +Backend β†’ http://localhost:5000 +``` + +Because the origins are different, the browser applies CORS rules. + +In Express: + +```javascript +const cors = require("cors"); + +app.use(cors({ + origin: "http://localhost:3000", + credentials: true +})); +``` + +Now the backend explicitly allows requests from that frontend origin. + +**Common CORS Headers** + +```text +Access-Control-Allow-Origin +Access-Control-Allow-Methods +Access-Control-Allow-Headers +Access-Control-Allow-Credentials +``` + +--- + +### 8. What is XSS? How do you prevent it? + +XSS (Cross-Site Scripting) is a vulnerability where an attacker manages to execute malicious JavaScript in another user's browser through content that the application renders as trusted content. It commonly occurs when untrusted user input is inserted into HTML or JavaScript without proper escaping or sanitization. + +For example, an attacker might submit malicious content into a comment field that another user later views. + +```html + +``` + +If the application renders that input as raw HTML, the browser may execute it. + +**Types of XSS** + +**Stored XSS** β€” Malicious content is stored on the server/database and later displayed to users. + +```text +Attacker + ↓ +Malicious Comment + ↓ +Database + ↓ +Victim opens page + ↓ +Script executes +``` + +**Reflected XSS** β€” Malicious input comes from a request and is immediately reflected into the response. + +**DOM-based XSS** β€” The vulnerability occurs through client-side JavaScript manipulating unsafe input in the DOM. + +**How to Prevent XSS** + +- Escape user-controlled output. +- Avoid inserting untrusted HTML directly. +- Sanitize HTML when HTML input is genuinely required. +- Use frameworks' default escaping mechanisms. +- Use a strong Content Security Policy where appropriate. +- Validate input, while remembering that validation alone is not a substitute for output encoding. +- Use HttpOnly cookies for sensitive authentication credentials when appropriate. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Does HttpOnly completely prevent XSS?" *No. HttpOnly prevents JavaScript from directly reading that cookie, which helps protect session credentials from being stolen through XSS. However, XSS can still execute malicious actions in the user's browser, so the application must still prevent the underlying XSS vulnerability.* + +--- + +### 9. What is CSRF? How do you prevent it? + +CSRF (Cross-Site Request Forgery) is an attack where a malicious website causes a user's browser to send an unwanted authenticated request to another website where the user is already logged in. It is particularly relevant when authentication credentials such as cookies are automatically attached to requests. + +For example, a victim is logged into a banking website and then visits an attacker's website that attempts to trigger an unwanted transfer request to the bank. + +**Attack Flow** + +```text +User logged into Bank + ↓ +Visits malicious website + ↓ +Malicious site triggers request + ↓ +Browser automatically sends authentication cookie + ↓ +Bank receives authenticated request +``` + +**How to Prevent CSRF** + +**1. SameSite Cookies** β€” Configure authentication cookies appropriately: + +```javascript +res.cookie("session", token, { + httpOnly: true, + secure: true, + sameSite: "strict" +}); +``` + +SameSite restricts when browsers send cookies in cross-site contexts. + +**2. CSRF Tokens** β€” The server generates a unique token that the legitimate application must include with state-changing requests. + +```text +Server β†’ CSRF Token +Client β†’ Sends Token +Server β†’ Validates Token +``` + +**3. Validate Origin/Referer** β€” For sensitive operations, servers can also validate request origin information as an additional defense. + +**Important Difference** + +- **XSS** β†’ Attacker executes JavaScript in your application's context. +- **CSRF** β†’ Attacker tricks your browser into making an authenticated request. + +--- + +### 10. What is Rate Limiting? + +Rate limiting is a security and performance technique that restricts how many requests a client can make to an API within a specific time period. It helps protect APIs from brute-force attacks, abuse, denial-of-service attempts, accidental traffic spikes, and excessive resource consumption. + +For example, we might allow a user to make 100 requests per minute. If the limit is exceeded, the server returns HTTP `429 Too Many Requests`. + +**Example** + +```javascript +const rateLimit = require("express-rate-limit"); + +const limiter = rateLimit({ + windowMs: 60 * 1000, + max: 100, + message: "Too many requests, please try again later." +}); + +app.use("/api", limiter); +``` + +Here, each client can make up to 100 requests within a one-minute window. + +**Rate Limiting Flow** + +```text +Client + ↓ +Request + ↓ +Rate Limiter + ↓ +Under Limit? + ↙ β†˜ +Yes No + ↓ ↓ +API 429 +``` + +**Common Rate-Limiting Strategies** + +- **Fixed Window** β€” Allows a fixed number of requests during a fixed time window. (100 requests / 1 minute) +- **Sliding Window** β€” Tracks requests over a continuously moving time period and generally provides smoother limiting behavior. +- **Token Bucket** β€” Clients consume tokens for requests, while tokens are replenished at a configured rate. This can allow controlled bursts while maintaining an overall rate. + +--- + +## 🐳 Deployment & DevOps (5) + +### 1. What is Docker? + +Docker is a containerization platform that packages an application together with its dependencies, configuration, and runtime into a portable container image. A container runs as an isolated process using the host operating system's kernel, which makes it lighter and generally faster to start than a traditional virtual machine. + +Docker helps solve the "works on my machine" problem because the same image can be used across development, testing, and production environments. + +**Dockerfile Example** + +```dockerfile +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "start"] +``` + +Build the image: + +```bash +docker build -t my-app . +``` + +Run a container: + +```bash +docker run -p 3000:3000 my-app +``` + +**Image vs Container** + +- **Image** β†’ A read-only template/package containing the application and its dependencies. +- **Container** β†’ A running instance of an image. + +```text +Dockerfile + ↓ +Docker Image + ↓ +Docker Container +``` + +**Docker vs Virtual Machine** + +| Docker Container | Virtual Machine | +| --- | --- | +| Shares host kernel | Has its own guest OS | +| Lightweight | Heavier | +| Starts quickly | Usually slower to start | +| Lower resource overhead | Higher resource overhead | +| Packages application + dependencies | Includes complete OS | + +--- + +### 2. Explain a CI/CD Pipeline. + +CI/CD stands for **Continuous Integration** and **Continuous Delivery or Deployment**. It automates the process of taking code changes from a developer's repository through building, testing, and deployment, reducing manual errors and allowing applications to be released more reliably. + +CI focuses on frequently integrating code and automatically validating it through builds and tests. CD automates the process of preparing or deploying validated changes to staging or production. + +**Typical Pipeline** + +```text +Developer + ↓ +git push + ↓ +GitHub + ↓ +CI/CD Tool + ↓ +Install Dependencies + ↓ +Lint / Test + ↓ +Build + ↓ +Docker Image + ↓ +Push to Registry + ↓ +Deploy + ↓ +Production +``` + +**Example with Jenkins** + +```text +GitHub + ↓ +Jenkins + ↓ +npm install + ↓ +npm test + ↓ +docker build + ↓ +docker push + ↓ +Deploy to Server +``` + +A Jenkins pipeline might contain stages such as: + +```groovy +pipeline { + stages { + stage('Test') { + steps { + sh 'npm test' + } + } + + stage('Build') { + steps { + sh 'docker build -t my-app .' + } + } + + stage('Deploy') { + steps { + sh './deploy.sh' + } + } + } +} +``` + +--- + +### 3. What is a Reverse Proxy? (e.g., Nginx) + +A reverse proxy is a server that sits between clients and backend servers and forwards incoming requests to the appropriate backend service. Nginx is commonly used as a reverse proxy because it can efficiently handle HTTP traffic, TLS termination, load balancing, caching, and static files. + +Instead of clients directly accessing an internal Node.js server such as `localhost:3000`, they communicate with Nginx, which forwards the request to the application. + +**Example** + +```text +Client + ↓ +https://example.com + ↓ +Nginx + ↓ +Node.js :3000 +``` + +**Basic Nginx Configuration** + +```nginx +server { + listen 80; + + server_name example.com; + + location / { + proxy_pass http://localhost:3000; + } +} +``` + +Now: + +```text +GET https://example.com/users + ↓ + Nginx + ↓ +localhost:3000/users +``` + +**Why use Nginx?** + +- Reverse proxy +- SSL/TLS termination +- Load balancing +- Static file serving +- Request routing +- Caching +- Hiding internal application servers + +**Reverse Proxy vs Forward Proxy** + +- **Forward Proxy:** `Client β†’ Proxy β†’ Internet` +- **Reverse Proxy:** `Internet β†’ Proxy β†’ Backend Servers` + +A forward proxy represents the client side, while a reverse proxy represents the server side. + +--- + +### 4. Horizontal Scaling vs Vertical Scaling. + +Vertical scaling means increasing the resources of a single server, such as adding more CPU, RAM, or storage. Horizontal scaling means adding more server instances and distributing traffic between them. + +For example, upgrading a server from 4 CPUs and 8 GB RAM to 16 CPUs and 32 GB RAM is vertical scaling, while running four application instances behind a load balancer is horizontal scaling. + +**Vertical Scaling** + +Before: + +```text +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Server β”‚ +β”‚ 4 CPU / 8 GB β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +After: + +```text +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Server β”‚ +β”‚ 16 CPU / 32 GB β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Horizontal Scaling** + +```text + Load Balancer + / | \ + ↓ ↓ ↓ + Server Server Server + 1 2 3 +``` + +**Difference** + +| Vertical Scaling | Horizontal Scaling | +| --- | --- | +| Increase server resources | Add more servers | +| Simpler initially | More distributed architecture | +| Has hardware limits | Can scale by adding instances | +| Can have a larger single-server failure impact | Can provide better redundancy | +| Often easier for small systems | Common for high-scale systems | + +**Important Requirement for Node.js Apps** + +When horizontally scaling a backend, the application should avoid relying on local in-memory state for shared information. + +For example: + +```text +Server 1 ─┐ +Server 2 ─┼── Redis +Server 3 β”€β”˜ +``` + +Redis can provide shared state for things such as sessions, caching, or rate-limit counters when the architecture requires it. + +--- + +> πŸ’‘ **Interview Tip:** If asked "Which is better?" *"Neither is universally better. Vertical scaling is simpler and can be effective for smaller systems, while horizontal scaling is more suitable when we need greater capacity, redundancy, and the ability to add instances as traffic grows. Large distributed applications commonly use horizontal scaling."* + +--- + +### 5. What is Redis? Why is it used for caching? + +Redis is an in-memory data store that supports data structures such as strings, hashes, lists, sets, and sorted sets. It is commonly used for caching, sessions, rate limiting, counters, queues, and other low-latency workloads. + +For caching, Redis stores frequently requested data in memory so the application can retrieve it much faster than repeatedly querying a persistent database such as MongoDB or PostgreSQL. + +**Without Redis** + +```text +Client + ↓ +Node.js + ↓ +MongoDB + ↓ +Response +``` + +Every request may require a database query. + +**With Redis** + +```text +Client + ↓ +Node.js + ↓ +Redis + ↓ +Cache Hit β†’ Response +``` + +If the data isn't cached: + +```text +Redis Cache Miss + ↓ + MongoDB + ↓ +Store Result in Redis + ↓ + Response +``` + +**Example** + +```javascript +const cachedUser = await redis.get(`user:${userId}`); + +if (cachedUser) { + return JSON.parse(cachedUser); +} + +const user = await User.findById(userId); + +await redis.set( + `user:${userId}`, + JSON.stringify(user), + { EX: 300 } +); + +return user; +``` + +Here, the cached user expires after 300 seconds. + +**Why Redis is fast** + +Redis primarily keeps active data in memory, which avoids the latency of repeatedly reading from disk-based persistent storage. It also provides efficient operations on several built-in data structures. + +--- + +## πŸ—οΈ System Design (7) + +The following system design questions are a checklist to practice and prepare. + +- [ ] Design a URL Shortener. +- [ ] Design a Real-Time Chat Application. +- [ ] Design a JWT Authentication Flow. +- [ ] Design a File Upload Service. +- [ ] Design a MongoDB Schema for a Social Media App. +- [ ] How would you scale a MERN application? +- [ ] Explain your project's architecture end-to-end. + +--- + +## πŸ“š Project Questions (Bonus) + +The following project-based questions are a checklist to practice and prepare. + +- [ ] Explain your latest project in detail. +- [ ] Why did you choose this architecture? +- [ ] Why did you choose MongoDB? +- [ ] Why did you choose React? +- [ ] Why did you choose Express? +- [ ] Why did you choose Node.js? +- [ ] Explain your authentication flow. +- [ ] Explain your database design. +- [ ] What was the hardest bug you solved? +- [ ] What would you improve if you rebuilt the project today? + +--- + +> 🎯 **Happy Interview Prep!** All the best for your MERN Stack interviews in 2026.