# Node Interview - Intermediate

1. **<mark>How do you debug a Node.js application?</mark>**  
    Debugging a Node.js application can be done in several ways, depending on your environment, tools, and the complexity of the application.  
    ***i) Use*** `console.log()` ***Statements***
    
    ```javascript
    console.log('User data:', user);
    ```
    
    ***ii) Use the Built-In Debugger***  
    Node.js has a built-in debugger you can run with
    
    ```javascript
    node inspect app.js
    /*
    c – Continue execution
    n – Step to next line
    s – Step into a function
    o – Step out of a function
    repl – Enter interactive mode
    */
    ```
    
    To set a breakpoint in code
    
    ```javascript
    debugger;
    ```
    
    ***iii) Debug with Chrome DevTools***  
    Start your app with the `--inspect` flag,
    
    ```javascript
    node --inspect app.js
    ```
    
    Open Chrome and go to `chrome://inspect`  
    Click “Open dedicated DevTools for Node”  
    You can now set breakpoints, step through code, inspect variables, and more.  
    ***iv) Use Logging Libraries***  
    Use structured logging tools for better insights: `winston, pino, debug`
    
    ***v) Unit Testing + Debugging***
    
    Write tests using: `mocha, jest, ava`
    
    Run tests in watch mode and debug failing ones.  
    ***vi) Monitor with External Tools***
    
    Use tools like,
    
    **PM2** (for production monitoring/debugging)
    
    **New Relic** / **Datadog** (for profiling and performance debugging)
    
2. **<mark>How do you handle errors in Node.js?</mark>**  
    Handling errors in Node.js is critical for building robust, maintainable applications. Node.js provides several mechanisms for error handling, depending on whether the code is synchronous, asynchronous, uses promises, or streams.  
    ***i) General Principles***  
    a) Always **handle errors explicitly**.
    
    b) Use **consistent patterns** across your application.
    
    c) Avoid crashing the app for recoverable errors.
    
    d) Use **centralized error handling** in web apps (like Express).  
    ***ii) Synchronous Code***  
    Use `try...catch` blocks
    
    ```javascript
    try {
      const data = fs.readFileSync('file.txt');
      console.log(data);
    } catch (err) {
      console.error('Error reading file:', err);
    }
    ```
    
    ***iii) Asynchronous Callbacks***
    
    Node uses the **error-first callback pattern** (`err` as the first parameter)
    
    ```javascript
    fs.readFile('file.txt', (err, data) => {
      if (err) {
        return console.error('Error reading file:', err);
      }
      console.log(data);
    });
    ```
    
    ***iv) Promises & Async/Await***
    
    Wrap your async code in `try...catch`
    
    ```javascript
    async function readFile() {
      try {
        const data = await fs.promises.readFile('file.txt', 'utf-8');
        console.log(data);
      } catch (err) {
        console.error('Error:', err.message);
      }
    }
    ```
    
    ***v) Express Error Handling***
    
    ```javascript
    // Error-handling middleware
    app.use((err, req, res, next) => {
      console.error(err.stack);
      res.status(500).json({ error: 'Something went wrong!' });
    });
    
    // Example route with error
    app.get('/', (req, res, next) => {
      try {
        throw new Error('Oops!');
      } catch (err) {
        next(err);
      }
    });
    ```
    
    ***vi) Global Error Handlers***  
    Use as a last resort—don’t rely on them for control flow.
    
    ```javascript
    process.on('uncaughtException', err => {
      console.error('Uncaught Exception:', err);
      process.exit(1);
    });
    
    process.on('unhandledRejection', err => {
      console.error('Unhandled Rejection:', err);
      process.exit(1);
    });
    ```
    
3. **<mark>How do you handle file uploads in a Node.js application?</mark>**  
    Handling file uploads in a Node.js application typically involves using middleware to parse `multipart/form-data`, the encoding used by browsers for file uploads. The most common and robust solution is the `multer` middleware when working with **Express.js**.  
    ***i) Install Required Packages***
    
    ```javascript
    npm install express multer
    ```
    
    ***ii) Set Up*** `multer` ***Middleware***
    
    ```javascript
    const express = require('express');
    const multer = require('multer');
    const path = require('path');
    
    const app = express();
    
    // Configure storage
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
        cb(null, 'uploads/'); // folder to store uploaded files
      },
      filename: function (req, file, cb) {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + path.extname(file.originalname)); // preserve original extension
      }
    });
    
    // Create the upload middleware
    const upload = multer({ storage: storage });
    
    // Route to handle upload
    app.post('/upload', upload.single('myFile'), (req, res) => {
      console.log(req.file); // file info
      res.send('File uploaded successfully!');
    });
    
    app.listen(3000, () => console.log('Server running on port 3000'));
    ```
    
    ***iii) Test with an HTML Form***
    
    ```xml
    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="myFile" />
      <button type="submit">Upload</button>
    </form>
    ```
    
    ***iv) Storage Options in*** `multer`  
    a) diskStorage - Store files locally on disk (as shown above)  
    b) memoryStorage - Store files in memory as Buffer objects  
    c) stream - Stream files to cloud storage or other APIs
    
4. **<mark>Explain the concept of callback functions in Node.js. Provide an example.</mark>**  
    In **Node.js**, a **callback function** is a function passed as an argument to another function and is **executed after that function completes**. This is a fundamental concept in asynchronous programming, which Node.js heavily relies on—especially for I/O operations like file reading, database access, or HTTP requests.  
    ***i) Why Use Callbacks?***  
    JavaScript in Node.js is **non-blocking** and **single-threaded**, so callbacks allow operations to run in the background and execute code once they're done—without stopping the rest of the program.  
    ***ii) Key Characteristics of Callbacks***  
    They are passed as arguments to functions.
    
    They are invoked **after** an asynchronous operation completes.
    
    Often follow the **error-first** pattern: `(err, result) => {}`  
    ***iii) Reading a File Using a Callback***
    
    ```javascript
    const fs = require('fs');
    
    fs.readFile('example.txt', 'utf8', (err, data) => {
      if (err) {
        return console.error('Error reading file:', err);
      }
      console.log('File contents:', data);
    });
    ```
    
    ***iv) Custom Callback Example***  
    You can define your own functions that accept callbacks,
    
    ```javascript
    function greetUser(name, callback) {
      console.log('Hello, ' + name);
      callback();
    }
    
    greetUser('Alice', () => {
      console.log('This is a callback function!');
    });
    ```
    
    ***v) Callback vs Promises (and Async/Await)***  
    Callbacks are foundational, but can lead to **callback hell** if nested too deeply
    
    ```javascript
    doSomething(arg1, (err, result1) => {
      doSomethingElse(result1, (err, result2) => {
        andThenThis(result2, (err, result3) => {
          // ... hard to manage
        });
      });
    });
    ```
    
5. **<mark>What is the Node.js event loop? How does it work?</mark>**  
    The **Node.js Event Loop** is the core mechanism that enables **non-blocking, asynchronous I/O** operations—even though JavaScript is **single-threaded**. It allows Node.js to perform many operations (like reading files, querying databases, or making HTTP requests) **without blocking the main thread**.  
    At its core, the event loop is a **loop that monitors and processes asynchronous events** in the background. It keeps the application running as long as there are callbacks to be executed.  
    ***i) How the Event Loop Works (High-Level Steps)***  
    When Node.js runs your code, it uses the event loop to handle operations:
    
    **a) Initialize**: Executes your code (e.g., setting timers, reading files, registering callbacks).
    
    **b) Poll**: Waits for incoming events (e.g., I/O, timers) and places them in the callback queue.
    
    **c) Callback Execution**: Executes the callback associated with each event.
    
    **d) Repeat**: Continues this process until no more work remains.  
    ***ii) Event Loop Phases (Simplified Overview)***  
    The event loop has several **phases** that run in order,  
    a) Timers - Executes `setTimeout()` and `setInterval()` callbacks  
    b) Pending Callbacks - Executes I/O callbacks deferred to the next loop  
    c) Idle / Prepare - Internal operations  
    d) Poll - Waits for new I/O events; executes I/O callbacks  
    e) Check - Executes `setImmediate()` callbacks  
    f) Close Callbacks - Executes callbacks like `socket.on('close', ...)`  
    ***iii) Example to Illustrate Timing***
    
    ```javascript
    setTimeout(() => console.log('timeout'), 0);
    setImmediate(() => console.log('immediate'));
    process.nextTick(() => console.log('nextTick'));
    console.log('sync');
    // console.log('sync') runs first (synchronous).
    // process.nextTick() runs before the next phase of the event loop.
    // setTimeout(..., 0) and setImmediate() run in different phases (timers vs check).
    ```
    
    ***iv) Key Benefits***  
    a) Non-blocking I/O: Efficiently handles thousands of connections.
    
    b) Lightweight: Fewer threads mean lower overhead.
    
    c) Great for I/O-bound apps: APIs, chat servers, streaming apps, etc.  
    ***v) Limitations***
    
    a) CPU-bound operations block the event loop.
    
    b) Heavy computation should be offloaded to worker threads or external services.
    
6. **<mark>How do you read and write files in Node.js?</mark>**  
    Reading and writing files in **Node.js** is done using the built-in `fs` (File System) module. You can perform file operations both **synchronously** (blocking) and **asynchronously** (non-blocking).  
    **Use async** (non-blocking) for most apps—especially servers and APIs.  
    **Use sync** (blocking) for simple CLI tools, config loading, or during app startup.  
    ***Best Practises***  
    a) Always **handle errors** using `try...catch` or error-first callbacks.
    
    b) Use the **async versions** to avoid blocking the event loop.
    
    c) Use `path.join()` to construct file paths reliably across platforms.
    
    d) Use the `fs.promises` API for cleaner async/await usage.
    
    ***Synchronous vs Asynchronous Operations***  
    **a) Synchronous (Blocking)**
    
    Methods end in `Sync`, e.g., `readFileSync`, `writeFileSync`.
    
    Blocking: halts the entire program until the operation finishes.
    
    Easier for scripting or startup tasks.  
    **b) Asynchronous (Non-blocking)**
    
    Do **not** block the event loop.
    
    Use a **callback** or **Promise** to handle the result.  
    ***i) Reading Files - Asynchronously (non-blocking, recommended)***
    
    ```javascript
    const fs = require('fs');
    
    fs.readFile('example.txt', 'utf8', (err, data) => {
      if (err) {
        return console.error('Error reading file:', err);
      }
      console.log('File content:', data);
    });
    ```
    
    ***ii) Reading Files - Synchronously (blocking)***
    
    ```javascript
    const data = fs.readFileSync('example.txt', 'utf8');
    console.log('File content:', data);
    ```
    
    ***iii) Reading Files - With Promises /*** `async/await`
    
    ```javascript
    const fs = require('fs').promises;
    
    async function readFile() {
      try {
        const data = await fs.readFile('example.txt', 'utf8');
        console.log('File content:', data);
      } catch (err) {
        console.error('Error:', err);
      }
    }
    
    readFile();
    ```
    
    ***iv) Writing Files - Asynchronously (non-blocking, recommended)***
    
    ```javascript
    fs.writeFile('output.txt', 'Hello, world!', err => {
      if (err) return console.error('Error writing file:', err);
      console.log('File written successfully!');
    });
    ```
    
    ***v) Writing Files - Synchronously (blocking)***
    
    ```javascript
    fs.writeFileSync('output.txt', 'Hello, world!');
    console.log('File written successfully!');
    ```
    
    ***vi) Reading Files - With Promises /*** `async/await`
    
    ```javascript
    const fs = require('fs').promises;
    
    async function writeFile() {
      try {
        await fs.writeFile('output.txt', 'Hello, world!');
        console.log('File written successfully!');
      } catch (err) {
        console.error('Error writing file:', err);
      }
    }
    
    writeFile();
    ```
    
7. **<mark>What is Express.js? How does it simplify building web applications with Node.js?</mark>**  
    **Express.js** is a **minimal and flexible web framework** for **Node.js** that provides powerful features to build web and mobile applications. It simplifies the process of creating server-side logic by abstracting much of the boilerplate code involved in handling HTTP requests, routing, middleware, and more.  
    Without Express, writing a web server in Node.js requires manually handling HTTP methods, headers, and routing logic using the `http` module. Express makes this much simpler and cleaner.  
    ***i) Key Features***  
    a) Routing - Easily map URLs to handlers (GET, POST, etc.)  
    b) Middleware - Process requests (authentication, logging, body parsing, etc.)  
    c) Template Engine Support - Supports Pug, EJS, Handlebars for rendering dynamic HTML  
    d) Static Files - Serve static assets like CSS, JS, images  
    e) Error Handling - Centralized and structured error handling  
    f) REST API Support - Ideal for building RESTful APIs  
    ***ii) Basic Example***
    
    ```javascript
    // Install Express
    npm install express
    
    // Simple Server
    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      res.send('Hello, World!');
    });
    
    app.listen(3000, () => {
      console.log('Server listening on port 3000');
    });
    ```
    
    ***iii) Routing Example***
    
    ```javascript
    app.get('/about', (req, res) => {
      res.send('About Page');
    });
    
    app.post('/submit', (req, res) => {
      res.send('Form Submitted');
    });
    ```
    
    ***iv) Middleware Example***
    
    ```javascript
    app.use(express.json()); // Parses JSON request bodies
    
    app.use((req, res, next) => {
      console.log(`${req.method} ${req.url}`);
      next(); // Pass control to the next handler
    });
    ```
    
    ***v) Error Handling Middleware***
    
    ```javascript
    app.use((err, req, res, next) => {
      console.error(err.stack);
      res.status(500).send('Something went wrong!');
    });
    ```
    
    ***vi) Benefits of Express.js***
    
    a) Great community and ecosystem
    
    b) Easy learning curve for JavaScript/Node developers
    
    c) Integrates well with databases and other Node.js libraries
    
    d) Foundation for many popular frameworks (like NestJS, Sails)
    
8. **<mark>How can you validate the input data in Node js Application?</mark>**  
    In a **Node.js application**, validating input data is crucial for **security, data integrity**, and **preventing bugs or injection attacks** (like SQL or XSS). There are several ways to handle validation depending on your stack and needs.  
    ***i) Using a Validation Library (Recommended)***
    
    ```javascript
    // Popular Choice: Joi
    npm install joi
    
    // example
    const Joi = require('joi');
    
    // Define schema
    const schema = Joi.object({
      username: Joi.string().alphanum().min(3).max(30).required(),
      age: Joi.number().integer().min(18).required(),
      email: Joi.string().email().required()
    });
    
    // Validate data
    const result = schema.validate({
      username: 'JohnDoe',
      age: 25,
      email: 'john@example.com'
    });
    
    if (result.error) {
      console.error('Validation error:', result.error.details);
    } else {
      console.log('Valid input!');
    }
    ```
    
    ***ii) With Express Middleware***  
    You can integrate Joi with Express to validate request bodies
    
    ```javascript
    const express = require('express');
    const Joi = require('joi');
    const app = express();
    
    app.use(express.json());
    
    const userSchema = Joi.object({
      name: Joi.string().required(),
      email: Joi.string().email().required()
    });
    
    app.post('/user', (req, res) => {
      const { error } = userSchema.validate(req.body);
      if (error) {
        return res.status(400).json({ message: error.details[0].message });
      }
      res.send('User data is valid');
    });
    ```
    
    ***iii) Using*** `express-validator`
    
    ```javascript
    // install express-validator
    npm install express-validator
    
    // example
    const { body, validationResult } = require('express-validator');
    
    app.post('/user',
      body('email').isEmail(),
      body('password').isLength({ min: 6 }),
      (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
          return res.status(400).json({ errors: errors.array() });
        }
        res.send('Input is valid');
      });
    ```
    
    ***iv) Manual Validation (Not Recommended for Complex Logic)***
    
    ```javascript
    if (!req.body.name || typeof req.body.name !== 'string') {
      return res.status(400).send('Name is required and must be a string');
    }
    ```
    
    ***v) Best Practices***
    
    a) Validate all **user inputs**: body, query, params, headers.
    
    b) **Use a schema-based library** (like Joi) for maintainability.
    
    c) Always **return clear error messages** to help with debugging (but avoid leaking sensitive info).
    
    d) Sanitize inputs if needed (`express-validator` offers sanitizers too).
    
9. **<mark>What is authentication? How is it implemented in Node.js applications?</mark>**
    
    **Authentication** is the process of **verifying the identity** of a user or system. In a Node.js application, it's used to confirm that a user is who they claim to be—usually by checking credentials like a username/password or a token.  
    ***i) Types of Authentication***  
    a) Session-Based Authentication (typically with cookies)
    
    b) Token-Based Authentication (e.g. JSON Web Tokens – JWT)
    
    c) OAuth / Third-party login (e.g. Google, Facebook)
    
    d) API Key Authentication
    
    ***ii) Common Authentication Implementation in Node.js***  
    a) Password hashing - `bcrypt` or `argon2`  
    b) JWT tokens - `jsonwebtoken`  
    c) Middleware - `passport` or custom  
    d) Sessions - `express-session`  
    ***iii) JWT Authentication – Common Approach***  
    Install Dependencies
    
    ```javascript
    npm install express jsonwebtoken bcryptjs
    ```
    
    User Login Flow
    
    ```javascript
    const express = require('express');
    const jwt = require('jsonwebtoken');
    const bcrypt = require('bcryptjs');
    const app = express();
    
    app.use(express.json());
    
    const users = [{ id: 1, username: 'admin', password: '$2a$10$...' }]; // hashed passwords
    
    app.post('/login', async (req, res) => {
      const { username, password } = req.body;
    
      const user = users.find(u => u.username === username);
      if (!user) return res.status(400).send('User not found');
    
      const valid = await bcrypt.compare(password, user.password);
      if (!valid) return res.status(401).send('Invalid password');
    
      const token = jwt.sign({ id: user.id }, 'secretKey', { expiresIn: '1h' });
      res.json({ token });
    });
    ```
    
    Protect Routes with Middleware
    
    ```javascript
    function authenticateToken(req, res, next) {
      const authHeader = req.headers['authorization'];
      const token = authHeader && authHeader.split(' ')[1]; // Bearer <token>
    
      if (!token) return res.sendStatus(401);
    
      jwt.verify(token, 'secretKey', (err, user) => {
        if (err) return res.sendStatus(403); // Invalid token
        req.user = user;
        next();
      });
    }
    
    // Protected route
    app.get('/dashboard', authenticateToken, (req, res) => {
      res.send('Protected dashboard content');
    });
    ```
    
    Best Practices -
    
    a) Hash passwords before storing (never store plain text).
    
    b) Store secrets securely (e.g. in `.env` files).
    
    c) Use HTTPS to protect token transmission.
    
    d) Implement token expiration and refresh where appropriate.
    
    e) Validate input data before authenticating.  
    ***iv) Session-Based Auth (with express-session)***
    
    ```javascript
    npm install express-session
    ```
    
    ```javascript
    const session = require('express-session');
    
    app.use(session({
      secret: 'yourSecret',
      resave: false,
      saveUninitialized: true,
      cookie: { secure: false } // use true with HTTPS
    }));
    ```
    
    Sessions store user identity on the server (e.g., in memory, Redis), and send a cookie to the browser.
    
10. **<mark>How do you handle CORS (Cross-Origin Resource Sharing) in a Node.js application?</mark>**  
    Handling **CORS (Cross-Origin Resource Sharing)** in a **Node.js** application is essential when your front-end and back-end are hosted on different origins (domains, ports, or protocols). CORS is a security feature enforced by browsers that restricts web pages from making requests to a different origin than the one that served the web page.  
    CORS allows a server to specify who can access its resources and which methods are allowed. If not handled correctly, the browser will block cross-origin requests from the client.  
    ***i) Install the*** `cors` ***package***
    
    ```javascript
    npm install cors
    ```
    
    ***ii) Basic Usage with Express***
    
    ```javascript
    const express = require('express');
    const cors = require('cors');
    
    const app = express();
    
    app.use(cors()); // Enable CORS for all origins
    
    app.get('/data', (req, res) => {
      res.json({ message: 'This is CORS-enabled for all origins!' });
    });
    
    app.listen(3000, () => console.log('Server running on port 3000'));
    ```
    
    ***iii) Restrict CORS to Specific Origin***
    
    ```javascript
    const corsOptions = {
      origin: 'https://example.com', // only allow this domain
      methods: ['GET', 'POST'],
      allowedHeaders: ['Content-Type', 'Authorization']
    };
    
    app.use(cors(corsOptions));
    ```
    
    ***iv) Enable CORS for a Single Route***
    
    ```javascript
    app.get('/public', cors(), (req, res) => {
      res.json({ message: 'CORS-enabled route' });
    });
    ```
    
    ***v) CORS with Credentials (cookies, auth headers)***
    
    ```javascript
    const corsOptions = {
      origin: 'http://localhost:3000',
      credentials: true
    };
    
    app.use(cors(corsOptions));
    ```
    
    Ensure your client-side request also includes,
    
    ```javascript
    fetch('http://localhost:5000/api', {
      credentials: 'include'
    });
    ```
    
    ***vi) Best Practices***
    
    a) Use `cors()` middleware for ease and flexibility.
    
    b) Be **specific** in production: avoid `*` for `Access-Control-Allow-Origin`.
    
    c) Enable **credentials** only when needed and configure `origin` accordingly.
    
    d) Be cautious with sensitive routes or APIs.
    
11. **<mark>Explain the concept of WebSockets and their Use in Node.js Applications</mark>**  
    **WebSockets** are a communication protocol that provides **full-duplex, bidirectional** communication between the **client** (e.g., browser) and **server** over a single, long-lived TCP connection.
    
    Unlike HTTP (which is **request-response-based**), WebSockets allow **real-time** communication without repeatedly reestablishing connections or polling.  
    Node.js is a great platform for WebSockets due to its **non-blocking, event-driven** nature.  
    Common Libraries -
    
    a) [`ws`](https://github.com/websockets/ws) – lightweight WebSocket implementation.  
    b) [`socket.io`](http://socket.io) [– hig](https://socket.io)her-level library, abstracts [ov](https://github.com/websockets/ws)er WebSocket with fall[ba](https://github.com/websockets/ws)ck support.  
    ***i) Key Feature***[***s of WebS***](https://socket.io)***ockets***
    
    [a](https://github.com/websockets/ws)) Pe[rsistent](https://socket.io) connection
    
    b) Low latency
    
    c) Real-t[ime updat](https://socket.io)es
    
    d) Uses `ws://` or `wss://` (secure WebSocket)  
    ***ii) When to Use WebSockets***
    
    a) Real-time chat apps
    
    b) Live notifications
    
    c) Real-time games
    
    d) Collaborative tools (e.g., Google Docs)
    
    e) Live stock/price updates  
    ***iii) Using*** `ws` ***(WebSocket)***  
    a) Installation
    
    ```javascript
    npm install ws
    ```
    
    b) Server (node js)
    
    ```javascript
    const WebSocket = require('ws');
    
    const wss = new WebSocket.Server({ port: 8080 });
    
    wss.on('connection', ws => {
      console.log('Client connected');
    
      ws.on('message', message => {
        console.log('Received:', message);
        ws.send(`Server echo: ${message}`);
      });
    
      ws.on('close', () => console.log('Client disconnected'));
    });
    ```
    
    c) Client (Browser)
    
    ```javascript
    <script>
      const socket = new WebSocket('ws://localhost:8080');
    
      socket.onopen = () => {
        socket.send('Hello Server!');
      };
    
      socket.onmessage = event => {
        console.log('From server:', event.data);
      };
    </script>
    ```
    
    ***iv) Using*** `socket.io` ***(More Feature-Rich)***  
    a) Installation
    
    ```javascript
    npm install socket.io
    ```
    
    b) Server
    
    ```javascript
    const http = require('http').createServer();
    const io = require('socket.io')(http);
    
    io.on('connection', socket => {
      console.log('User connected');
    
      socket.on('chat message', msg => {
        io.emit('chat message', msg);
      });
    
      socket.on('disconnect', () => console.log('User disconnected'));
    });
    
    http.listen(3000, () => console.log('Listening on *:3000'));
    ```
    
    c) Client
    
    ```javascript
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io('http://localhost:3000');
    
      socket.on('chat message', msg => {
        console.log('Message:', msg);
      });
    
      socket.emit('chat message', 'Hello from client');
    </script>
    ```
    
12. **<mark>What is Event Emitter in Node.js?</mark>**  
    In **Node.js**, the **EventEmitter** is a core module that enables an **event-driven architecture**. It allows objects (like servers, streams, etc.) to emit named events and for other parts of the code to listen and react to those events.
    
    This is fundamental to how Node.js handles asynchronous operations efficiently.  
    ***i) How It Works***  
    The `EventEmitter` class is part of the built-in `events` module.
    
    ```javascript
    const EventEmitter = require('events');
    const emitter = new EventEmitter();
    ```
    
    ***ii) Basic Example: Emitting and Listening***
    
    ```javascript
    // Register a listener
    emitter.on('greet', (name) => {
      console.log(`Hello, ${name}!`);
    });
    
    // Emit the event
    emitter.emit('greet', 'Alice');
    ```
    
    ***iii) Key Methods of EventEmitter***  
    a) on(event, listener) - Registers a listener (can run multiple times)  
    b) once(event, listener) - Runs the listener only once  
    c) emit(event, \[args\]) - Triggers the event, optionally passing data  
    d) removeListener() - Removes a specific listener  
    e) removeAllListeners() - Removes all listeners for an event  
    f) listenerCount(event) - Returns number of listeners for an event  
    ***iv) Common Use Cases in Node.js***  
    a) HTTP servers (`req.on('data')`, `res.on('finish')`)
    
    b) Streams (`readable.on('data')`)
    
    c) File system watcher
    
    d) Custom app-level events
    
13. **<mark>What are streams and what are types of streams available in Node.js?</mark>**  
    In **Node.js**, **streams** are powerful **abstract interfaces** for working with streaming data—**data that isn’t available all at once**, but instead arrives in chunks over time (e.g., reading a large file, streaming audio/video, receiving HTTP responses).
    
    Using streams helps improve **performance**, **memory usage**, and **scalability** in data-heavy applications.  
    ***i) What Are Streams?***
    
    A **stream** is like a **data pipeline** where you can read from a source or write to a destination **piece by piece**, instead of loading everything into memory at once.  
    ***ii) Four Main Types of Streams***  
    **a) Readable -** Can be read from  
    `fs.createReadStream()`, HTTP request  
    **b) Writable -** Can be written to  
    `fs.createWriteStream()`, HTTP response  
    **c) Duplex -** Can be read from and written to  
    TCP socket (`net.Socket`)  
    **d) Transform -** Duplex stream that can **modify data** as it passes through  
    zlib.createGzip()  
    ***iii) Example: Readable Stream***
    
    ```javascript
    const fs = require('fs');
    const readable = fs.createReadStream('large-file.txt');
    
    readable.on('data', chunk => {
      console.log('Received chunk:', chunk.length);
    });
    
    readable.on('end', () => {
      console.log('Finished reading file.');
    });
    ```
    
    ***iv) Example: Writable Stream***
    
    ```javascript
    const fs = require('fs');
    const writable = fs.createWriteStream('output.txt');
    
    writable.write('Hello, ');
    writable.write('world!');
    writable.end(); // Signals end of writing
    ```
    
    ***v) Example: Piping Readable → Writable***
    
    ```javascript
    const fs = require('fs');
    
    const readStream = fs.createReadStream('input.txt');
    const writeStream = fs.createWriteStream('output.txt');
    
    readStream.pipe(writeStream); // Transfers content
    ```
    
    ***vi) Example: Transform Stream (e.g., Compression)***
    
    ```javascript
    const fs = require('fs');
    const zlib = require('zlib');
    
    const gzip = zlib.createGzip();
    const input = fs.createReadStream('file.txt');
    const output = fs.createWriteStream('file.txt.gz');
    
    input.pipe(gzip).pipe(output);
    ```
    
    ***vii) Benefits of Streams***
    
    a) Memory efficient: Handle large files/data in small chunks.
    
    b) Faster: Start processing before the full data is available.
    
    c) Composable: Chain multiple streams together using `.pipe()`.
    
14. **<mark>What is Piping in Node?</mark>**  
    **Piping** in Node.js is a mechanism used with **streams** to **connect the output of one stream directly into the input of another**. This allows you to **chain operations** and pass data efficiently from one stream to another without manually handling data events.  
    **Piping** lets you transfer data from a **readable stream** to a **writable stream** automatically and efficiently.  
    **Piping** is a method to connect streams and transfer data.
    
    Use `.pipe()` to link a readable stream to a writable (or transform) stream.
    
    It’s commonly used for **file I/O**, **compression**, **HTTP responses**, and **data processing**.  
    ***i) Example: Reading from a File and Writing to Another***
    
    ```javascript
    const fs = require('fs');
    
    const readStream = fs.createReadStream('input.txt');
    const writeStream = fs.createWriteStream('output.txt');
    
    // Pipe the read stream into the write stream
    readStream.pipe(writeStream);
    ```
    
    This reads `input.txt` in chunks and writes them to `output.txt` **without loading the entire file into memory**.  
    ***ii) Chaining Multiple Pipes***  
    You can pipe through multiple **transform streams** to modify data.
    
    ```javascript
    const fs = require('fs');
    const zlib = require('zlib');
    
    const gzip = zlib.createGzip();
    const read = fs.createReadStream('file.txt');
    const write = fs.createWriteStream('file.txt.gz');
    
    read.pipe(gzip).pipe(write); // Compresses the file
    ```
    
    ***iii) Benefits of Piping***
    
    a) Memory Efficient – handles data in chunks.
    
    b) Readable & Writable – stream data from one place to another.
    
    c) Simplifies code – avoids manual `.on('data')` and `.write()` handling.
    
    d) Easy to compose – chain multiple streams together (e.g., transform, compress, log).
    
15. **<mark>What is the difference between setImmediate() and setTimeout()?</mark>**  
    The difference between `setImmediate()` and `setTimeout()` in Node.js lies in **when** they execute their callbacks in the **event loop**. Although both schedule asynchronous operations, their **timing behavior** is slightly different.  
    ***i) setTimeout(callback, 0)***  
    Executes the callback **after a minimum delay** of 0 milliseconds.
    
    The callback is scheduled in the **timers phase** of the event loop.
    
    Actual execution depends on the **system timer granularity** and current **event loop load**.
    
    ```javascript
    setTimeout(() => {
      console.log('setTimeout');
    }, 0);
    ```
    
    ***ii) setImmediate(callback)***  
    Executes the callback **immediately after the I/O events are processed**.
    
    The callback is scheduled in the **check phase** of the event loop.
    
    Typically runs **before** `setTimeout(..., 0)` if both are called in the same phase.
    
    ```javascript
    setImmediate(() => {
      console.log('setImmediate');
    });
    ```
    
16. **<mark>What is Swagger and How it helps to maintain the ReSTful API in Node js?</mark>**  
    **Swagger** is a **toolset and specification** for describing, documenting, testing, and visualizing **RESTful APIs**. It is now part of the **OpenAPI Specification (OAS)**.
    
    ***i) In Node.js, Swagger helps developers***
    
    a) Define API structure in a standardized way.
    
    b) Automatically generate interactive API documentation.
    
    c) Validate request/response schemas.
    
    d) Improve team collaboration and client integration.  
    ***ii) Key Benefits of Swagger in Node.js***  
    i) API documentation - Generates live, interactive docs (like Swagger UI)  
    ii) API validation - Validates input/output based on schema  
    iii) Client SDK generation - Auto-generates client SDKs in various languages  
    iv) Testing support - Allows testing APIs directly from the docs  
    v) Improves communication - Easy for frontend/backend teams or 3rd parties to understand APIs  
    ***iii) Install Required Packages***
    
    ```javascript
    npm install swagger-jsdoc swagger-ui-express
    ```
    
    ***iv) Set Up Swagger in*** `app.js` ***or*** `server.js`
    
    ```javascript
    const express = require('express');
    const swaggerUi = require('swagger-ui-express');
    const swaggerJsdoc = require('swagger-jsdoc');
    
    const app = express();
    
    const swaggerDefinition = {
      openapi: '3.0.0',
      info: {
        title: 'Example API',
        version: '1.0.0',
        description: 'A simple Express API with Swagger',
      },
      servers: [
        {
          url: 'http://localhost:3000',
        },
      ],
    };
    
    const options = {
      swaggerDefinition,
      apis: ['./routes/*.js'], // Path to the API docs
    };
    
    const swaggerSpec = swaggerJsdoc(options);
    
    app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
    ```
    
    ***v) Document Your Routes with JSDoc-style Comments***
    
    ```javascript
    /**
     * @swagger
     * /hello:
     *   get:
     *     summary: Returns a greeting
     *     responses:
     *       200:
     *         description: A simple message
     */
    app.get('/hello', (req, res) => {
      res.send('Hello Swagger!');
    });
    ```
    
    ***vi) Access Swagger UI***
    
    Navigate to, `http://localhost:3000/api-docs`[  
    ](http://localhost:3000/api-docs￼You’ll)You’ll see a live, interactive documentation page for your API.
    
17. **<mark>What is Redis and How it helps to cache the API resposne in Node js?</mark>**  
    **Redis** (Remote Dictionary Server) is a fast, **in-memory key-value data store** often used as:
    
    **Cache, Database, Message broker**
    
    In **Node.js**, Redis is commonly used to **cache API responses** to improve performance and reduce load on databases or third-party APIs.  
    ***i) Why Use Redis for Caching?***  
    a) Extremely Fast - Keeps data in memory (not disk) for ultra-low latency  
    b) Avoids Repetition - Prevents repeated database or API calls  
    c) Reduces Latency - Speeds up response time for frequently requested data  
    d) TTL Support - Auto-expire cached data using Time-To-Live  
    e) Scalable - Works well with distributed systems  
    ***ii) How to Use Redis for Caching in a Node.js API***  
    **a) Install Redis and Redis Client**
    
    Install Redis on your system (or use a service like Redis Cloud).
    
    Then install the Node.js client,
    
    ```javascript
    npm install redis
    ```
    
    **b) Connect to Redis**
    
    ```javascript
    const redis = require('redis');
    const client = redis.createClient();
    client.connect().then(() => console.log('Redis connected'));
    ```
    
    **c) Cache API Response Example**
    
    ```javascript
    const express = require('express');
    const app = express();
    
    const axios = require('axios');
    
    app.get('/weather/:city', async (req, res) => {
      const { city } = req.params;
    
      const cacheKey = `weather:${city}`;
    
      // 1. Check cache
      const cached = await client.get(cacheKey);
      if (cached) {
        return res.json({ source: 'cache', data: JSON.parse(cached) });
      }
    
      // 2. If not cached, fetch from API
      const apiResponse = await axios.get(`https://api.weatherapi.com/v1/current.json?key=YOUR_KEY&q=${city}`);
    
      // 3. Store response in Redis for 1 hour
      await client.setEx(cacheKey, 3600, JSON.stringify(apiResponse.data));
    
      res.json({ source: 'API', data: apiResponse.data });
    });
    ```
    
    ***iii) Common Use Cases of Redis Caching in Node.js***  
    a) API rate limiting
    
    b) Session storage
    
    c) Frequently accessed DB queries
    
    d) Third-party API responses (e.g., weather, stock prices)
    
    e) Full-page or partial-page caching
    
18. **<mark>How do you manage dependencies in a Node.js project to prevent compatibility issues or security vulnerabilities?</mark>**  
    Managing dependencies in a Node.js project is critical for **stability**, **security**, and **maintainability**. Here's how you can do it effectively,  
    ***i) Use*** `package.json` ***and*** `package-lock.json`  
    a) `package.json` defines project dependencies, versions, scripts, and metadata.
    
    b) `package-lock.json` locks the exact versions of installed packages to ensure consistent installs across environments.  
    **Best Practices -**
    
    a) Never delete `package-lock.json`
    
    b) Commit it to version control (Git)  
    ***ii) Use Semantic Versioning Wisely***
    
    ```javascript
    // In package.json
    "express": "^4.17.1"
    /*
    ^ allows minor and patch upgrades
    ~ allows only patch upgrades
    No prefix = locked version
    */
    ```
    
    a) Be cautious with `^` and `~`—unexpected updates may introduce breaking changes.
    
    b) Use `npm install <pkg>@version` to install specific versions if needed.  
    ***iii) Audit for Security Vulnerabilities***
    
    ```javascript
    npm audit
    ```
    
    a) Scans dependencies for known vulnerabilities.
    
    b) Lists severity (Low, Moderate, High, Critical).
    
    ```javascript
    npm audit fix
    ```
    
    ***iv) Regularly Update Dependencies***  
    Use tools like: [`npm outdated`](https://docs.npmjs.com/cli/v10/commands/npm-outdated) or [`npm-check-updates`](https://www.npmjs.com/package/npm-check-updates) (`ncu`)
    
    ```javascript
    npx npm-check-updates -u
    npm install
    ```
    
    ***v) Remove Unused or Obsolete Packages***
    
    ```javascript
    npm prune
    ```
    
    ***vi) Use a*** `.npmrc` ***File for Config***
    
    ```javascript
    // To lock down behavior
    save-exact=true
    // This will save packages without the ^ symbol, e.g., "express": "4.17.1".
    ```
    
    ***vii) Use CI/CD for Testing Compatibility***
    
    a) Run `npm ci` in CI pipelines for clean, deterministic installs.
    
    b) Use automated tests to catch issues from dependency updates.  
    ***viii) Consider Using Dependency Management Tools***  
    a) Dependabot (GitHub) – automatically creates PRs for outdated or vulnerable packages.
    
    b) Snyk – finds and fixes vulnerabilities.
    
    c) Renovate – manages version updates.
    
19. **<mark>Explain the OS module in Node.js?</mark>**  
    The `os` module in Node.js is a **built-in module** that provides a way to **interact with the operating system**. It allows you to retrieve system-related information such as the hostname, memory, CPU details, network interfaces, and more.
    
    You don’t need to install it — it's part of Node.js core.
    
    ```javascript
    const os = require('os');
    ```
    
    ***Example***
    
    ```javascript
    const os = require('os');
    
    console.log('Hostname:', os.hostname()); // Returns the hostname of the OS
    console.log('Platform:', os.platform()); // Returns the platform (e.g., 'linux', 'win32')
    console.log('CPU Architecture:', os.arch()); // Returns CPU architecture (e.g., 'x64')
    console.log('Total Memory (MB):', os.totalmem() / 1024 / 1024); // Total system memory (in bytes)
    console.log('Free Memory (MB):', os.freemem() / 1024 / 1024); // Free system memory (in bytes)
    console.log('Uptime (minutes):', os.uptime() / 60); // System uptime in seconds
    console.log('User Info:', os.userInfo()); // 	Info about the currently logged-in user
    ```
    
    ***Use Cases in Real Applications***
    
    a) System monitoring dashboards
    
    b) Logging OS info in backend apps
    
    c) Performance tuning and diagnostics
    
    d) Load balancing based on CPU/memory
    
    e) Temp file storage (via `os.tmpdir()`)
    
20. **<mark>What is a Node Inspector?</mark>**
    
    **Node Inspector** is a debugging tool for Node.js applications that allows you to **debug your code using Chrome DevTools** or other browser-based interfaces. It provides a visual way to:
    
    a) Set breakpoints
    
    b) Step through code
    
    c) Inspect variables and call stack
    
    d) Monitor memory usage
    
    It connects to the **V8 Inspector Protocol**, which Node.js supports natively.  
    ***i) Why Use Node Inspector?***
    
    a) Visual Debugging - Easier than console.log debugging  
    b) Step Execution - Pause, resume, step into/over functions  
    c) Variable Inspection - See scope, values, closures in real time  
    d) Live Code Editing - Modify code while paused (in Chrome DevTools)  
    e) Heap & CPU Profiling - Useful for performance tuning  
    ***ii) How to Use Node Inspector (Built-In Since Node.js v6.3+)***  
    **a) Run your script with the** `--inspect` **flag**
    
    ```javascript
    node --inspect index.js
    ```
    
    **b) Open Chrome and Go to**
    
    ```javascript
    chrome://inspect
    ```
    
    Then click **“Open dedicated DevTools for Node”**.
    
    You’ll now see Chrome DevTools connected to your Node.js app.  
    **c) Set Breakpoints and Debug**
    
    Use the **Sources** tab to browse your code
    
    Set breakpoints by clicking line numbers
    
    Use the **Console**, **Scope**, and **Call Stack** panels for full control
