<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ashish's Blog]]></title><description><![CDATA[Ashish's Blog]]></description><link>https://blog.ashish.pro</link><generator>RSS for Node</generator><lastBuildDate>Mon, 18 May 2026 02:07:59 GMT</lastBuildDate><atom:link href="https://blog.ashish.pro/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[JWT Authentication in Node.js Explained Simply]]></title><description><![CDATA[Your application has user accounts. Some routes should only be accessible to logged-in users. How do you protect routes and verify that a request actually comes from who they claim to be?
JWT (JSON We]]></description><link>https://blog.ashish.pro/jwt-authentication-nodejs-explained</link><guid isPermaLink="true">https://blog.ashish.pro/jwt-authentication-nodejs-explained</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Tue, 05 May 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/802313b0-3973-432a-a07d-6106e4a8839a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your application has user accounts. Some routes should only be accessible to logged-in users. How do you protect routes and verify that a request actually comes from who they claim to be?</p>
<p>JWT (JSON Web Token) is a popular, stateless way to handle authentication. Once a user logs in, you give them a token. They include this token with every request, and you verify it to confirm they're authenticated.</p>
<h2>Why Authentication Exists</h2>
<p>Imagine a banking app. You log in, and your account is yours alone. If anyone could access anyone else's account, the app would be useless and insecure.</p>
<p>Authentication answers one question: "Who are you?" Once you've answered that question by logging in, the app trusts that you are who you claim to be.</p>
<h2>What Is JWT?</h2>
<p>JWT is a self-contained token that includes:</p>
<ol>
<li><p>User information (claims)</p>
</li>
<li><p>A signature to verify it hasn't been tampered with</p>
</li>
<li><p>An expiry time so old tokens eventually become invalid</p>
</li>
</ol>
<p>It's like a digital ID card that proves you are who you say you are.</p>
<h2>JWT Structure</h2>
<p>A JWT has three parts separated by dots:</p>
<pre><code class="language-plaintext">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
</code></pre>
<p><strong>Part 1: Header</strong></p>
<pre><code class="language-json">{
  "alg": "HS256",
  "typ": "JWT"
}
</code></pre>
<p>This tells you the algorithm used to sign the token and that it's a JWT.</p>
<p><strong>Part 2: Payload (Claims)</strong></p>
<pre><code class="language-json">{
  "userId": 123,
  "username": "john",
  "email": "john@example.com",
  "iat": 1516239022,
  "exp": 1516325422
}
</code></pre>
<p>This is the data about the user. <code>iat</code> is when it was issued, <code>exp</code> is when it expires.</p>
<p><strong>Part 3: Signature</strong></p>
<pre><code class="language-plaintext">SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
</code></pre>
<p>This is created by signing the header and payload with a secret key. It prevents tampering.</p>
<h2>Login Flow with JWT</h2>
<p>Here's how JWT authentication works:</p>
<ol>
<li><p><strong>User logs in</strong> → Sends username and password</p>
</li>
<li><p><strong>Server validates</strong> → Checks credentials in database</p>
</li>
<li><p><strong>Server creates token</strong> → Encodes user info into JWT</p>
</li>
<li><p><strong>Server returns token</strong> → Sends JWT to client</p>
</li>
<li><p><strong>Client stores token</strong> → Saves it in localStorage or a cookie</p>
</li>
<li><p><strong>Client sends token</strong> → Includes it with every request</p>
</li>
<li><p><strong>Server verifies token</strong> → Checks signature and expiry</p>
</li>
<li><p><strong>Server grants access</strong> → If valid, allows the request</p>
</li>
</ol>
<h2>Setting Up JWT in Express</h2>
<p>First, install the jsonwebtoken package:</p>
<pre><code class="language-bash">npm install jsonwebtoken
</code></pre>
<p>Here's a basic example:</p>
<pre><code class="language-javascript">import express from 'express';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

const SECRET_KEY = 'your-secret-key-keep-it-safe';

// Login route
app.post('/login', (req, res) =&gt; {
  // In real apps, validate username/password against database
  const user = {
    userId: 123,
    username: 'john',
    email: 'john@example.com'
  };

  // Create JWT token
  const token = jwt.sign(user, SECRET_KEY, { expiresIn: '1h' });

  res.json({ message: 'Login successful', token });
});

app.listen(3000, () =&gt; {
  console.log('Server running');
});
</code></pre>
<p>When someone posts to <code>/login</code>, they receive a JWT token.</p>
<h2>Verifying Tokens</h2>
<p>Now protect routes by checking if the token is valid:</p>
<pre><code class="language-javascript">import express from 'express';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

const SECRET_KEY = 'your-secret-key-keep-it-safe';

// Middleware to verify token
function verifyToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

app.post('/login', (req, res) =&gt; {
  const user = { userId: 123, username: 'john' };
  const token = jwt.sign(user, SECRET_KEY, { expiresIn: '1h' });
  res.json({ token });
});

// Protected route
app.get('/profile', verifyToken, (req, res) =&gt; {
  res.json({ message: `Welcome ${req.user.username}` });
});

app.listen(3000);
</code></pre>
<p>The <code>verifyToken</code> middleware checks the token before allowing access to <code>/profile</code>.</p>
<h2>How to Send a Token</h2>
<p>After login, clients include the token in the Authorization header:</p>
<pre><code class="language-javascript">// Client-side (in browser or mobile app)
const token = 'eyJhbGc...'; // Token received from login

fetch('http://localhost:3000/profile', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(res =&gt; res.json())
.then(data =&gt; console.log(data));
</code></pre>
<p>The format is: <code>Authorization: Bearer [token]</code></p>
<p>The server extracts the token after the word "Bearer".</p>
<h2>Complete Authentication Example</h2>
<p>Here's a complete login and protected route system:</p>
<pre><code class="language-javascript">import express from 'express';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

const SECRET_KEY = 'super-secret-key';

// Sample user database
const users = [
  { id: 1, username: 'john', password: 'password123' },
  { id: 2, username: 'jane', password: 'password456' }
];

// Middleware to verify token
function verifyToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
}

// Login route
app.post('/login', (req, res) =&gt; {
  const { username, password } = req.body;

  // Find user (in real apps, check against hashed passwords)
  const user = users.find(u =&gt; u.username === username &amp;&amp; u.password === password);

  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Create token
  const token = jwt.sign(
    { userId: user.id, username: user.username },
    SECRET_KEY,
    { expiresIn: '24h' }
  );

  res.json({ message: 'Login successful', token });
});

// Protected route
app.get('/profile', verifyToken, (req, res) =&gt; {
  res.json({
    message: `Hello ${req.user.username}`,
    userId: req.user.userId
  });
});

// Another protected route
app.get('/dashboard', verifyToken, (req, res) =&gt; {
  res.json({ message: 'Dashboard for ' + req.user.username });
});

app.listen(3000, () =&gt; {
  console.log('Auth server running');
});
</code></pre>
<h2>Token Expiry</h2>
<p>Tokens expire to limit damage if a token is stolen. Create tokens with an expiry:</p>
<pre><code class="language-javascript">const token = jwt.sign(user, SECRET_KEY, { expiresIn: '1h' });
</code></pre>
<p>After 1 hour, the token is invalid, and the user must log in again.</p>
<p>When someone uses an expired token, <code>jwt.verify</code> throws an error, and they get a 401 response.</p>
<h2>Refresh Tokens</h2>
<p>For better user experience, use refresh tokens. These are long-lived tokens that generate new short-lived access tokens:</p>
<pre><code class="language-javascript">app.post('/login', (req, res) =&gt; {
  const user = { userId: 123, username: 'john' };

  // Short-lived access token
  const accessToken = jwt.sign(user, SECRET_KEY, { expiresIn: '15m' });

  // Long-lived refresh token
  const refreshToken = jwt.sign(user, REFRESH_SECRET, { expiresIn: '7d' });

  res.json({ accessToken, refreshToken });
});

app.post('/refresh', (req, res) =&gt; {
  const { refreshToken } = req.body;

  try {
    const decoded = jwt.verify(refreshToken, REFRESH_SECRET);
    const newAccessToken = jwt.sign(
      { userId: decoded.userId, username: decoded.username },
      SECRET_KEY,
      { expiresIn: '15m' }
    );
    res.json({ accessToken: newAccessToken });
  } catch (error) {
    res.status(401).json({ error: 'Invalid refresh token' });
  }
});
</code></pre>
<p>Users use the short-lived access token for requests. When it expires, they use the refresh token to get a new access token without logging in again.</p>
<h2>Important Security Practices</h2>
<p><strong>Keep your secret safe:</strong> Never commit it to Git. Use environment variables:</p>
<pre><code class="language-javascript">const SECRET_KEY = process.env.JWT_SECRET;
</code></pre>
<p><strong>Use HTTPS only:</strong> Always transmit tokens over HTTPS to prevent interception.</p>
<p><strong>Don't store sensitive data in tokens:</strong> JWT tokens are encoded, not encrypted. Anyone can decode them and read the payload. Don't include passwords or payment info.</p>
<p><strong>Set reasonable expiry times:</strong> Shorter expiry means less damage if a token is stolen. Balance with user convenience.</p>
<p><strong>Hash passwords:</strong> Never store plain passwords in your database. Always hash them.</p>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Storing passwords in JWT</strong></p>
<pre><code class="language-javascript">// Wrong
const token = jwt.sign({
  username: user.username,
  password: user.password
}, SECRET_KEY);

// Right
const token = jwt.sign({
  userId: user.id,
  username: user.username
}, SECRET_KEY);
</code></pre>
<p><strong>Mistake 2: Using a weak secret</strong></p>
<pre><code class="language-javascript">// Wrong - too simple
const SECRET_KEY = 'secret';

// Better - long random string
const SECRET_KEY = process.env.JWT_SECRET;
</code></pre>
<p><strong>Mistake 3: Not checking token expiry</strong></p>
<p><code>jwt.verify</code> handles expiry checking automatically. Don't skip it.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>JWT is a self-contained token with user information and a signature</p>
</li>
<li><p>Users get a token after login and include it with every request</p>
</li>
<li><p>Tokens expire to limit damage if stolen</p>
</li>
<li><p>Verify tokens with <code>jwt.verify</code> before allowing access</p>
</li>
<li><p>Use middleware to check tokens on protected routes</p>
</li>
<li><p>Keep your secret key safe and use environment variables</p>
</li>
<li><p>Tokens are encoded, not encrypted, so don't store sensitive data</p>
</li>
<li><p>Use refresh tokens for better user experience with short-lived access tokens</p>
</li>
</ul>
<p>JWT is a powerful, flexible way to handle authentication in Node.js applications. Once you understand the basics, you can build secure APIs.</p>
]]></content:encoded></item><item><title><![CDATA[Sessions vs JWT vs Cookies: Understanding Authentication Approaches]]></title><description><![CDATA[Your application needs to know who's accessing it. When a user logs in, you need to remember they're authenticated so they don't have to log in again for every request. There are three main ways to do]]></description><link>https://blog.ashish.pro/sessions-jwt-cookies-authentication-nodejs</link><guid isPermaLink="true">https://blog.ashish.pro/sessions-jwt-cookies-authentication-nodejs</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Mon, 04 May 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/44a6b89c-b792-4fe4-ba85-3dade2014139.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your application needs to know who's accessing it. When a user logs in, you need to remember they're authenticated so they don't have to log in again for every request. There are three main ways to do this: sessions, cookies, and JWT tokens. Each has different tradeoffs.</p>
<h2>What Are Cookies?</h2>
<p>Cookies are small pieces of data stored on the client's browser. When you set a cookie, the browser automatically sends it with every request to your server.</p>
<p>Think of cookies like a stamp on your hand at an amusement park. Once you get the stamp, you show it to staff at each ride. The staff don't need to check your ticket every time.</p>
<p>Here's how cookies work:</p>
<ol>
<li><p>Server sends a Set-Cookie header in the response</p>
</li>
<li><p>Browser stores the cookie</p>
</li>
<li><p>Browser automatically includes the cookie in every request back to the server</p>
</li>
</ol>
<pre><code class="language-javascript">// Setting a cookie in Express
res.cookie('username', 'john', { httpOnly: true, maxAge: 3600000 });
res.send('Cookie set');
</code></pre>
<p>Cookies are just a transport mechanism. By themselves, they don't authenticate anyone. They need to work with either sessions or JWT to be useful.</p>
<h2>What Are Sessions?</h2>
<p>A session is server-side storage of user information. When a user logs in, the server creates a session and stores data about that user. The server then sends a session ID to the client, which stores it in a cookie.</p>
<p><strong>How session-based authentication works:</strong></p>
<ol>
<li><p>User logs in with username and password</p>
</li>
<li><p>Server validates credentials and creates a session</p>
</li>
<li><p>Server stores session data (like user ID) in memory or a database</p>
</li>
<li><p>Server sends session ID in a cookie</p>
</li>
<li><p>Browser includes the session cookie in every request</p>
</li>
<li><p>Server looks up the session ID to find user information</p>
</li>
</ol>
<p>This is stateful authentication. The server maintains state about who's logged in.</p>
<pre><code class="language-javascript">import express from 'express';
import session from 'express-session';

const app = express();

app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: { maxAge: 1000 * 60 * 60 * 24 } // 24 hours
}));

app.post('/login', (req, res) =&gt; {
  // Verify user credentials
  const userId = 123;
  
  // Store in session
  req.session.userId = userId;
  res.send('Logged in');
});

app.get('/profile', (req, res) =&gt; {
  if (req.session.userId) {
    res.send(`Welcome user ${req.session.userId}`);
  } else {
    res.status(401).send('Not logged in');
  }
});
</code></pre>
<h2>What is JWT?</h2>
<p>JWT stands for JSON Web Token. It's a self-contained token that includes all the information the server needs to authenticate a user. Instead of storing session data on the server, you encode the data into the token itself.</p>
<p><strong>JWT structure:</strong></p>
<p>A JWT has three parts separated by dots: <code>header.payload.signature</code></p>
<pre><code class="language-plaintext">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
</code></pre>
<ul>
<li><p><strong>Header</strong>: Specifies the algorithm used to sign the token</p>
</li>
<li><p><strong>Payload</strong>: Contains claims (data) like user ID and username</p>
</li>
<li><p><strong>Signature</strong>: Ensures the token hasn't been tampered with</p>
</li>
</ul>
<p>Here's how JWT authentication works:</p>
<ol>
<li><p>User logs in with username and password</p>
</li>
<li><p>Server validates credentials</p>
</li>
<li><p>Server creates a JWT with user information</p>
</li>
<li><p>Server sends JWT to the client</p>
</li>
<li><p>Client stores JWT (usually in localStorage or a cookie)</p>
</li>
<li><p>Client sends JWT in the Authorization header with each request</p>
</li>
<li><p>Server verifies the signature and extracts user information</p>
</li>
</ol>
<pre><code class="language-javascript">import express from 'express';
import jwt from 'jsonwebtoken';

const app = express();
const SECRET_KEY = 'your-secret-key';

app.post('/login', (req, res) =&gt; {
  // Verify user credentials
  const userId = 123;
  const username = 'john';
  
  // Create JWT
  const token = jwt.sign(
    { userId, username },
    SECRET_KEY,
    { expiresIn: '24h' }
  );
  
  res.json({ token });
});

app.get('/profile', (req, res) =&gt; {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).send('No token');
  }
  
  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    res.send(`Welcome ${decoded.username}`);
  } catch (error) {
    res.status(401).send('Invalid token');
  }
});
</code></pre>
<h2>Sessions vs JWT: Key Differences</h2>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>Sessions</th>
<th>JWT</th>
</tr>
</thead>
<tbody><tr>
<td><strong>State</strong></td>
<td>Stateful (server keeps data)</td>
<td>Stateless (data in token)</td>
</tr>
<tr>
<td><strong>Storage</strong></td>
<td>Server (memory/database)</td>
<td>Client (localStorage/cookie)</td>
</tr>
<tr>
<td><strong>Scalability</strong></td>
<td>Harder to scale across multiple servers</td>
<td>Easy to scale (no server state)</td>
</tr>
<tr>
<td><strong>Logout</strong></td>
<td>Easy (delete session)</td>
<td>Harder (token valid until expiry)</td>
</tr>
<tr>
<td><strong>Bandwidth</strong></td>
<td>Small cookie sent</td>
<td>Larger token sent</td>
</tr>
<tr>
<td><strong>Mobile</strong></td>
<td>Works with cookies</td>
<td>Better for APIs</td>
</tr>
</tbody></table>
<h2>When to Use Each</h2>
<p><strong>Use Sessions When:</strong></p>
<ul>
<li><p>You're building a traditional web application with server-rendered pages</p>
</li>
<li><p>You need immediate logout capability</p>
</li>
<li><p>You're running on a single server or have a shared session store</p>
</li>
<li><p>You want simpler implementation</p>
</li>
</ul>
<p><strong>Use JWT When:</strong></p>
<ul>
<li><p>You're building a mobile app or single-page application</p>
</li>
<li><p>You have multiple servers that need to authenticate users independently</p>
</li>
<li><p>You want a stateless architecture</p>
</li>
<li><p>You're building a public API</p>
</li>
</ul>
<p><strong>Use Cookies When:</strong></p>
<ul>
<li><p>You're setting session IDs or tokens</p>
</li>
<li><p>You need automatic browser handling</p>
</li>
<li><p>You're building traditional web apps</p>
</li>
</ul>
<h2>Common Security Practices</h2>
<p><strong>For Sessions:</strong></p>
<ul>
<li><p>Use secure, HttpOnly cookies</p>
</li>
<li><p>Set appropriate session timeout</p>
</li>
<li><p>Regenerate session ID on login</p>
</li>
<li><p>Use HTTPS only</p>
</li>
</ul>
<p><strong>For JWT:</strong></p>
<ul>
<li><p>Sign tokens with a strong secret</p>
</li>
<li><p>Use HTTPS to prevent token interception</p>
</li>
<li><p>Set reasonable expiry times</p>
</li>
<li><p>Consider using refresh tokens for longer sessions</p>
</li>
<li><p>Keep the secret safe</p>
</li>
</ul>
<h2>Real-World Example Comparison</h2>
<p>Let's say you have a simple blogging platform. Here's how each approach handles a request flow:</p>
<p><strong>Session-based flow:</strong></p>
<ol>
<li><p>User logs in → Server creates session with user ID</p>
</li>
<li><p>User requests profile → Server checks session, recognizes user</p>
</li>
<li><p>User logs out → Server deletes session</p>
</li>
</ol>
<p><strong>JWT-based flow:</strong></p>
<ol>
<li><p>User logs in → Server creates token with user ID</p>
</li>
<li><p>User requests profile → Token sent with request, server verifies signature</p>
</li>
<li><p>User logs out → Client simply deletes token</p>
</li>
</ol>
<h2>Key Takeaways</h2>
<ul>
<li><p>Cookies are just a transport mechanism, not authentication by themselves</p>
</li>
<li><p>Sessions are stateful: server stores user information</p>
</li>
<li><p>JWT is stateless: user information is stored in the token itself</p>
</li>
<li><p>Sessions are easier for traditional web apps and immediate logout</p>
</li>
<li><p>JWT scales better for distributed systems and APIs</p>
</li>
<li><p>Both can use cookies for transport; the difference is what data is stored where</p>
</li>
</ul>
<p>Choose the approach based on your application architecture. Traditional web apps often use sessions, while modern APIs and mobile apps commonly use JWT.</p>
]]></content:encoded></item><item><title><![CDATA[REST API Design Made Simple with Express.js]]></title><description><![CDATA[APIs are how applications communicate. A REST API is a structured way to request and receive data over HTTP. Building REST APIs is one of the most common uses for Express.
What is an API?
An API (Appl]]></description><link>https://blog.ashish.pro/rest-api-design-expressjs</link><guid isPermaLink="true">https://blog.ashish.pro/rest-api-design-expressjs</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Sun, 03 May 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/93b18055-85d1-4acd-8767-d9ee494dccff.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>APIs are how applications communicate. A REST API is a structured way to request and receive data over HTTP. Building REST APIs is one of the most common uses for Express.</p>
<h2>What is an API?</h2>
<p>An API (Application Programming Interface) is a contract between client and server. It says: "Send me this format, and I'll respond with that format."</p>
<p>For example, a social media API might say: "POST to <code>/posts</code> with a message, and I'll create a post."</p>
<h2>What is REST?</h2>
<p>REST (Representational State Transfer) is a style of designing APIs. It uses HTTP methods (GET, POST, PUT, DELETE) and URLs to represent operations on resources.</p>
<p>A resource is something you can create, read, update, or delete. Examples: users, posts, comments, products.</p>
<p>Instead of having endpoints like:</p>
<ul>
<li><code>/createUser</code></li>
<li><code>/getUserData</code></li>
<li><code>/deleteUser</code></li>
</ul>
<p>REST uses the resource URL with HTTP methods:</p>
<ul>
<li><code>GET /users</code> - Get all users</li>
<li><code>POST /users</code> - Create a user</li>
<li><code>GET /users/123</code> - Get user 123</li>
<li><code>PUT /users/123</code> - Update user 123</li>
<li><code>DELETE /users/123</code> - Delete user 123</li>
</ul>
<p>This is cleaner and more logical.</p>
<h2>HTTP Methods for CRUD</h2>
<p>REST maps HTTP methods to database operations:</p>
<table>
<thead>
<tr>
<th>HTTP Method</th>
<th>Operation</th>
<th>Meaning</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GET</strong></td>
<td>Read</td>
<td>Retrieve data</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td>Create</td>
<td>Add new data</td>
</tr>
<tr>
<td><strong>PUT</strong></td>
<td>Update</td>
<td>Modify existing data</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td>Delete</td>
<td>Remove data</td>
</tr>
</tbody></table>
<p>These four methods handle all basic operations (CRUD: Create, Read, Update, Delete).</p>
<h2>Basic REST API Example</h2>
<p>Here's a simple REST API for managing blog posts:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();
app.use(express.json());

// Sample data
let posts = [
  { id: 1, title: 'First Post', content: 'Hello world' },
  { id: 2, title: 'Second Post', content: 'Another post' }
];

// GET all posts
app.get('/posts', (req, res) =&gt; {
  res.json(posts);
});

// GET a single post
app.get('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }
  res.json(post);
});

// CREATE a new post
app.post('/posts', (req, res) =&gt; {
  const newPost = {
    id: posts.length + 1,
    title: req.body.title,
    content: req.body.content
  };
  posts.push(newPost);
  res.status(201).json(newPost);
});

// UPDATE a post
app.put('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }
  post.title = req.body.title || post.title;
  post.content = req.body.content || post.content;
  res.json(post);
});

// DELETE a post
app.delete('/posts/:id', (req, res) =&gt; {
  const index = posts.findIndex(p =&gt; p.id === parseInt(req.params.id));
  if (index === -1) {
    return res.status(404).json({ error: 'Post not found' });
  }
  const deleted = posts.splice(index, 1);
  res.json({ message: 'Post deleted', post: deleted[0] });
});

app.listen(3000);
</code></pre>
<p>This API covers all CRUD operations for posts.</p>
<h2>Naming Conventions</h2>
<p>Follow these conventions for clean URLs:</p>
<ul>
<li><strong>Use plural nouns for resources</strong>: <code>/posts</code>, <code>/users</code>, <code>/products</code> (not <code>/post</code>, <code>/user</code>)</li>
<li><strong>Use lowercase</strong>: <code>/posts</code> (not <code>/Posts</code>)</li>
<li><strong>Use hyphens for multi-word resources</strong>: <code>/blog-posts</code> (not <code>/blogposts</code> or <code>/blog_posts</code>)</li>
<li><strong>Avoid verbs in URLs</strong>: Use HTTP methods instead<ul>
<li>Wrong: <code>GET /posts/getAll</code></li>
<li>Right: <code>GET /posts</code></li>
</ul>
</li>
</ul>
<h2>Status Codes</h2>
<p>Use appropriate HTTP status codes:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Meaning</th>
<th>Use When</th>
</tr>
</thead>
<tbody><tr>
<td><strong>200</strong></td>
<td>OK</td>
<td>Request succeeded</td>
</tr>
<tr>
<td><strong>201</strong></td>
<td>Created</td>
<td>Resource created successfully</td>
</tr>
<tr>
<td><strong>400</strong></td>
<td>Bad Request</td>
<td>Client error (invalid data)</td>
</tr>
<tr>
<td><strong>401</strong></td>
<td>Unauthorized</td>
<td>Authentication required</td>
</tr>
<tr>
<td><strong>403</strong></td>
<td>Forbidden</td>
<td>Authenticated but not allowed</td>
</tr>
<tr>
<td><strong>404</strong></td>
<td>Not Found</td>
<td>Resource doesn't exist</td>
</tr>
<tr>
<td><strong>500</strong></td>
<td>Server Error</td>
<td>Server-side error</td>
</tr>
</tbody></table>
<pre><code class="language-javascript">app.get('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }
  res.status(200).json(post); // 200 is default, can omit
});

app.post('/posts', (req, res) =&gt; {
  const newPost = { id: posts.length + 1, ...req.body };
  posts.push(newPost);
  res.status(201).json(newPost);
});
</code></pre>
<h2>Handling Query Parameters</h2>
<p>Use query parameters for filtering and sorting:</p>
<pre><code class="language-javascript">app.get('/posts', (req, res) =&gt; {
  let results = posts;

  // Filter by status
  if (req.query.status) {
    results = results.filter(p =&gt; p.status === req.query.status);
  }

  // Sort by date
  if (req.query.sort === 'date') {
    results.sort((a, b) =&gt; new Date(b.date) - new Date(a.date));
  }

  // Pagination
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const start = (page - 1) * limit;
  results = results.slice(start, start + limit);

  res.json(results);
});
</code></pre>
<p>Usage:</p>
<ul>
<li><code>GET /posts?status=published</code></li>
<li><code>GET /posts?sort=date</code></li>
<li><code>GET /posts?page=2&amp;limit=5</code></li>
</ul>
<h2>Error Handling</h2>
<p>Consistent error responses help clients:</p>
<pre><code class="language-javascript">app.get('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).json({
      error: {
        code: 'POST_NOT_FOUND',
        message: 'The requested post does not exist',
        details: `Post with ID ${req.params.id} not found`
      }
    });
  }
  res.json(post);
});
</code></pre>
<p>Structured error responses let clients handle errors properly.</p>
<h2>Request Validation</h2>
<p>Validate data before processing:</p>
<pre><code class="language-javascript">app.post('/posts', (req, res) =&gt; {
  const { title, content } = req.body;

  // Validation
  if (!title || !content) {
    return res.status(400).json({
      error: 'Title and content are required'
    });
  }

  if (title.length &lt; 3) {
    return res.status(400).json({
      error: 'Title must be at least 3 characters'
    });
  }

  const newPost = { id: posts.length + 1, title, content };
  posts.push(newPost);
  res.status(201).json(newPost);
});
</code></pre>
<h2>API Versioning</h2>
<p>As your API grows, you might need to change it. Versioning helps:</p>
<pre><code class="language-javascript">app.get('/v1/posts', (req, res) =&gt; {
  // Version 1 implementation
  res.json(posts);
});

app.get('/v2/posts', (req, res) =&gt; {
  // Version 2 implementation (different format, more data, etc.)
  res.json(posts.map(p =&gt; ({
    id: p.id,
    title: p.title,
    description: p.content,
    createdAt: new Date()
  })));
});
</code></pre>
<p>This lets old clients continue using v1 while new clients use v2.</p>
<h2>Authentication in REST APIs</h2>
<p>Protect routes with authentication:</p>
<pre><code class="language-javascript">function authenticate(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  // Verify token (simplified)
  req.user = { id: 1 };
  next();
}

app.post('/posts', authenticate, (req, res) =&gt; {
  // Create post as authenticated user
  const newPost = {
    id: posts.length + 1,
    ...req.body,
    userId: req.user.id
  };
  posts.push(newPost);
  res.status(201).json(newPost);
});

app.delete('/posts/:id', authenticate, (req, res) =&gt; {
  // Delete logic
});
</code></pre>
<p>Only authenticated users can create or delete.</p>
<h2>CORS for Cross-Origin Requests</h2>
<p>If your API is called from a different domain, enable CORS:</p>
<pre><code class="language-bash">npm install cors
</code></pre>
<pre><code class="language-javascript">import cors from 'cors';

app.use(cors());
// or restrict to specific domains
app.use(cors({
  origin: 'https://example.com'
}));
</code></pre>
<h2>Complete REST API Example</h2>
<p>Here's a realistic REST API with authentication, validation, and proper status codes:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();
app.use(express.json());

let posts = [];
let nextId = 1;

// Middleware: log requests
app.use((req, res, next) =&gt; {
  console.log(`\({req.method} \){req.path}`);
  next();
});

// Middleware: authenticate
function authenticate(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (token === 'valid-token') {
    req.user = { id: 1 };
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
}

// GET all posts
app.get('/posts', (req, res) =&gt; {
  res.json(posts);
});

// GET single post
app.get('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) return res.status(404).json({ error: 'Not found' });
  res.json(post);
});

// POST create post (requires auth)
app.post('/posts', authenticate, (req, res) =&gt; {
  const { title, content } = req.body;

  if (!title || !content) {
    return res.status(400).json({ error: 'Title and content required' });
  }

  const newPost = { id: nextId++, title, content, userId: req.user.id };
  posts.push(newPost);
  res.status(201).json(newPost);
});

// PUT update post
app.put('/posts/:id', authenticate, (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) return res.status(404).json({ error: 'Not found' });

  post.title = req.body.title || post.title;
  post.content = req.body.content || post.content;
  res.json(post);
});

// DELETE post
app.delete('/posts/:id', authenticate, (req, res) =&gt; {
  const index = posts.findIndex(p =&gt; p.id === parseInt(req.params.id));
  if (index === -1) return res.status(404).json({ error: 'Not found' });

  posts.splice(index, 1);
  res.json({ message: 'Deleted' });
});

app.listen(3000);
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li>REST uses HTTP methods (GET, POST, PUT, DELETE) to represent operations</li>
<li>Resources are nouns, not verbs: <code>/posts</code> not <code>/getPosts</code></li>
<li>Status codes indicate operation success: 200, 201, 400, 404, 500</li>
<li>Query parameters filter and sort results</li>
<li>Validation catches bad data before processing</li>
<li>Authentication protects sensitive operations</li>
<li>Consistent error responses help clients handle errors</li>
<li>Versioning lets you evolve your API without breaking clients</li>
</ul>
<p>Build REST APIs following these principles, and you'll create clean, intuitive, maintainable APIs that developers love to use.</p>
]]></content:encoded></item><item><title><![CDATA[Handling File Uploads in Express with Multer]]></title><description><![CDATA[Users need to upload files: profile pictures, documents, resumes, attachments. Handling this in Express requires special handling. A library called Multer makes it straightforward.
Why File Uploads Ne]]></description><link>https://blog.ashish.pro/handling-file-uploads-express-multer</link><guid isPermaLink="true">https://blog.ashish.pro/handling-file-uploads-express-multer</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Sat, 02 May 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/a007c42a-03bf-4c50-bf83-65a4e0dcb0fd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Users need to upload files: profile pictures, documents, resumes, attachments. Handling this in Express requires special handling. A library called Multer makes it straightforward.</p>
<h2>Why File Uploads Need Special Handling</h2>
<p>Regular form data is text. But files are binary. When uploading files, the request format changes to <code>multipart/form-data</code>, which handles both text fields and binary files.</p>
<p>Express doesn't automatically parse this format. That's where Multer comes in.</p>
<h2>What is Multer?</h2>
<p>Multer is middleware for Express that handles file uploads. It parses multipart form data, extracts files, and saves them to disk or memory.</p>
<p>Install it:</p>
<pre><code class="language-bash">npm install multer
</code></pre>
<h2>Basic File Upload</h2>
<p>Set up Multer to handle a single file:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).send('No file uploaded');
  }
  res.send(`File ${req.file.filename} uploaded`);
});

app.listen(3000);
</code></pre>
<ul>
<li><p><code>multer({ dest: 'uploads/' })</code> saves files to the <code>uploads/</code> folder</p>
</li>
<li><p><code>upload.single('file')</code> handles one file from a form field named "file"</p>
</li>
<li><p><code>req.file</code> contains file information</p>
</li>
</ul>
<p>A user posts a file to <code>/upload</code>, and Multer saves it to <code>uploads/</code>.</p>
<h2>Accessing File Information</h2>
<p>After upload, <code>req.file</code> contains:</p>
<pre><code class="language-javascript">{
  fieldname: 'file',
  originalname: 'photo.jpg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  destination: 'uploads/',
  filename: 'file-1234567890',
  path: 'uploads/file-1234567890',
  size: 154321
}
</code></pre>
<p>Use this information to track what was uploaded:</p>
<pre><code class="language-javascript">app.post('/upload', upload.single('file'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).send('No file uploaded');
  }

  res.json({
    message: 'File uploaded successfully',
    filename: req.file.filename,
    originalName: req.file.originalname,
    size: req.file.size,
    path: req.file.path
  });
});
</code></pre>
<h2>Multiple File Uploads</h2>
<p>Handle multiple files with <code>upload.array()</code>:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload-multiple', upload.array('files'), (req, res) =&gt; {
  if (!req.files || req.files.length === 0) {
    return res.status(400).send('No files uploaded');
  }

  const fileInfo = req.files.map(file =&gt; ({
    filename: file.filename,
    originalName: file.originalname,
    size: file.size
  }));

  res.json({ files: fileInfo });
});

app.listen(3000);
</code></pre>
<p><code>req.files</code> is an array of uploaded files.</p>
<p>You can limit the number of files:</p>
<pre><code class="language-javascript">upload.array('files', 5) // Max 5 files
</code></pre>
<h2>Customizing File Names</h2>
<p>By default, Multer generates random filenames. Configure storage to control this:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';
import path from 'path';

const app = express();

const storage = multer.diskStorage({
  destination: (req, file, cb) =&gt; {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) =&gt; {
    // Keep original extension
    const ext = path.extname(file.originalname);
    const name = path.basename(file.originalname, ext);
    const uniqueName = name + '-' + Date.now() + ext;
    cb(null, uniqueName);
  }
});

const upload = multer({ storage });

app.post('/upload', upload.single('file'), (req, res) =&gt; {
  res.json({ filename: req.file.filename });
});

app.listen(3000);
</code></pre>
<p>Now files are named: <code>photo-1234567890.jpg</code> instead of random names.</p>
<h2>Filtering File Types</h2>
<p>Validate file types to prevent unwanted uploads:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';

const app = express();

const storage = multer.diskStorage({
  destination: 'uploads/',
  filename: (req, file, cb) =&gt; {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const fileFilter = (req, file, cb) =&gt; {
  // Allow only images
  const allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
  
  if (allowedMimes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Only image files are allowed'));
  }
};

const upload = multer({ storage, fileFilter });

app.post('/upload-image', upload.single('image'), (req, res) =&gt; {
  res.json({ message: 'Image uploaded', filename: req.file.filename });
});

app.listen(3000);
</code></pre>
<p>The <code>fileFilter</code> function checks the MIME type and rejects non-image files.</p>
<h2>Limiting File Size</h2>
<p>Prevent huge files from being uploaded:</p>
<pre><code class="language-javascript">const upload = multer({
  dest: 'uploads/',
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB
  }
});

app.post('/upload', upload.single('file'), (req, res) =&gt; {
  res.json({ message: 'File uploaded' });
});
</code></pre>
<p>Files larger than 5MB are rejected.</p>
<h2>Handling Multiple File Fields</h2>
<p>Upload different types of files in one request:</p>
<pre><code class="language-javascript">const app = express();

const upload = multer({ dest: 'uploads/' });

const fileUpload = upload.fields([
  { name: 'profilePic', maxCount: 1 },
  { name: 'coverPhoto', maxCount: 1 },
  { name: 'documents', maxCount: 5 }
]);

app.post('/upload-profile', fileUpload, (req, res) =&gt; {
  const profilePic = req.files.profilePic?.[0];
  const coverPhoto = req.files.coverPhoto?.[0];
  const documents = req.files.documents;

  res.json({
    profilePic: profilePic?.filename,
    coverPhoto: coverPhoto?.filename,
    documentCount: documents?.length || 0
  });
});

app.listen(3000);
</code></pre>
<p><code>req.files</code> contains objects for each field name.</p>
<h2>Complete Realistic Example</h2>
<p>Here's a complete user profile upload system:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.use(express.json());

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) =&gt; {
    const uploadDir = 'uploads/profiles';
    cb(null, uploadDir);
  },
  filename: (req, file, cb) =&gt; {
    const ext = path.extname(file.originalname);
    const name = 'profile-' + Date.now() + ext;
    cb(null, name);
  }
});

// Filter to allow only images
const fileFilter = (req, file, cb) =&gt; {
  const allowedMimes = ['image/jpeg', 'image/png'];
  if (allowedMimes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Only JPEG and PNG images allowed'));
  }
};

const upload = multer({
  storage,
  fileFilter,
  limits: { fileSize: 2 * 1024 * 1024 } // 2MB
});

// Serve uploaded files
app.use('/images', express.static('uploads/profiles'));

// Upload endpoint
app.post('/upload-profile', upload.single('profilePic'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }

  const imageUrl = `/images/${req.file.filename}`;

  res.json({
    message: 'Profile picture uploaded',
    filename: req.file.filename,
    url: imageUrl,
    size: req.file.size
  });
});

// Error handler for Multer
app.use((err, req, res, next) =&gt; {
  if (err instanceof multer.MulterError) {
    if (err.code === 'FILE_TOO_LARGE') {
      return res.status(400).json({ error: 'File too large' });
    }
    return res.status(400).json({ error: err.message });
  }
  if (err) {
    return res.status(400).json({ error: err.message });
  }
  next();
});

app.listen(3000, () =&gt; {
  console.log('Server running on port 3000');
});
</code></pre>
<h2>Memory Storage</h2>
<p>Store files in memory instead of disk (useful for processing before saving):</p>
<pre><code class="language-javascript">const storage = multer.memoryStorage();
const upload = multer({ storage });

app.post('/process-file', upload.single('file'), (req, res) =&gt; {
  // File is in req.file.buffer (as a Buffer)
  const content = req.file.buffer.toString();
  res.json({ message: 'File processed' });
});
</code></pre>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Not handling errors</strong></p>
<pre><code class="language-javascript">// Wrong - crashes on file too large
app.post('/upload', upload.single('file'), (req, res) =&gt; {
  res.send('Done');
});

// Right - add error handler
app.use((err, req, res, next) =&gt; {
  if (err instanceof multer.MulterError) {
    res.status(400).json({ error: err.message });
  } else {
    res.status(500).json({ error: 'Server error' });
  }
});
</code></pre>
<p><strong>Mistake 2: Trusting original filename</strong></p>
<pre><code class="language-javascript">// Wrong - security risk
const filename = file.originalname;

// Right - generate safe filename
const filename = Date.now() + '-' + file.originalname;
</code></pre>
<p><strong>Mistake 3: Not validating file type</strong></p>
<p>Always validate using MIME type or file extension, preferably both.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>Multer is middleware for handling file uploads in Express</p>
</li>
<li><p>Use <code>upload.single()</code> for one file, <code>upload.array()</code> for multiple</p>
</li>
<li><p>Configure storage to control where and how files are saved</p>
</li>
<li><p>Use fileFilter to validate file types</p>
</li>
<li><p>Set size limits to prevent huge uploads</p>
</li>
<li><p>Always generate unique, safe filenames</p>
</li>
<li><p>Use error handlers to gracefully handle upload errors</p>
</li>
<li><p>Serve uploaded files as static files with <code>express.static</code></p>
</li>
</ul>
<p>File uploads are common in web applications. Master Multer, and you can confidently handle user-uploaded content.</p>
]]></content:encoded></item><item><title><![CDATA[Storing Uploaded Files and Serving Them in Express]]></title><description><![CDATA[Users need to upload files to your application. Maybe they're uploading profile pictures, documents, or media. Once you store these files, you need to serve them back to users. This requires understan]]></description><link>https://blog.ashish.pro/storing-uploading-serving-files-express</link><guid isPermaLink="true">https://blog.ashish.pro/storing-uploading-serving-files-express</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Fri, 01 May 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/e07b77cb-38ec-4187-b191-a8cc0e1d2311.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Users need to upload files to your application. Maybe they're uploading profile pictures, documents, or media. Once you store these files, you need to serve them back to users. This requires understanding where files go and how to make them accessible.</p>
<h2>Where Do Uploaded Files Live?</h2>
<p>When a user uploads a file, it doesn't magically appear on the web. You need to decide where to store it.</p>
<p>There are two main approaches:</p>
<p><strong>Local Storage (on your server):</strong> Files are saved to a folder on your server's disk. Your server then serves these files directly to users.</p>
<p><strong>External Storage (cloud):</strong> Files are uploaded to a service like AWS S3, Google Cloud Storage, or Cloudinary. Your server doesn't store them; the cloud service does.</p>
<p>We'll focus on local storage first, as it's simpler to understand and perfect for learning.</p>
<h2>Setting Up a Local Upload Folder</h2>
<p>Create a folder where uploads will be stored:</p>
<pre><code class="language-plaintext">project/
  uploads/
  server.js
  package.json
</code></pre>
<p>This <code>uploads</code> folder holds all user files.</p>
<h2>Handling File Uploads with Multer</h2>
<p>Express doesn't handle file uploads by default. You need middleware like Multer.</p>
<p>First, install Multer:</p>
<pre><code class="language-bash">npm install multer
</code></pre>
<p>Here's how to set up basic file uploads:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) =&gt; {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) =&gt; {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({ storage });

app.post('/upload', upload.single('file'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).send('No file uploaded');
  }
  res.json({ message: 'File uploaded', filename: req.file.filename });
});

app.listen(3000, () =&gt; console.log('Server running'));
</code></pre>
<p>When a user uploads a file:</p>
<ol>
<li><p>Multer intercepts the request</p>
</li>
<li><p>Saves the file to the <code>uploads/</code> folder with a unique name</p>
</li>
<li><p>Adds file information to <code>req.file</code></p>
</li>
<li><p>Your route handler processes the request</p>
</li>
</ol>
<h2>Serving Uploaded Files</h2>
<p>Now users need to access these files. You'll serve them as static files.</p>
<p>Express has a built-in middleware for this:</p>
<pre><code class="language-javascript">import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Serve files from uploads folder
app.use('/files', express.static(path.join(__dirname, 'uploads')));

app.listen(3000, () =&gt; console.log('Server running'));
</code></pre>
<p>Now files are accessible at <code>http://localhost:3000/files/filename.jpg</code></p>
<p>If a file is saved as <code>profile-1234567890.jpg</code> in the <code>uploads/</code> folder, it's accessible at:</p>
<pre><code class="language-plaintext">http://localhost:3000/files/profile-1234567890.jpg
</code></pre>
<h2>Complete Upload and Serve Example</h2>
<p>Here's a complete example with upload and serving:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) =&gt; {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) =&gt; {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({ storage });

// Serve uploaded files as static
app.use('/files', express.static(path.join(__dirname, 'uploads')));

app.post('/upload-profile', upload.single('profilePic'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  
  const fileUrl = `/files/${req.file.filename}`;
  res.json({ 
    message: 'Profile picture uploaded',
    url: fileUrl 
  });
});

app.listen(3000, () =&gt; console.log('Server running'));
</code></pre>
<p>A user uploads a file → Multer saves it → Server returns the file URL → User can access it immediately.</p>
<h2>Upload Process Visualization</h2>
<pre><code class="language-plaintext">Client                          Server                      Disk
  |                               |                          |
  |--- POST /upload (file) -----&gt; |                          |
  |     (multipart/form-data)     |                          |
  |                               |-- Multer processes ------&gt;|
  |                               |    saves file             |
  |                               |                          |
  |&lt;-- 200 OK + filename ---------|                          |
  |     { url: '/files/...' }     |                          |
  |                               |                          |
  |-- GET /files/filename.jpg --&gt; |                          |
  |                               |-- Read from disk ------&gt; |
  |&lt;-- 200 + file data -----------|&lt;-- Return file data ------|
</code></pre>
<h2>Security Considerations</h2>
<p><strong>Validate file types:</strong></p>
<pre><code class="language-javascript">const upload = multer({
  storage,
  fileFilter: (req, file, cb) =&gt; {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('Invalid file type'));
    }
  }
});
</code></pre>
<p><strong>Limit file size:</strong></p>
<pre><code class="language-javascript">const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 } // 5MB
});
</code></pre>
<p><strong>Never trust the original filename:</strong></p>
<p>Always generate a new filename (like we did with timestamps and random numbers). If you use the original filename, malicious users could upload files with paths like <code>../../important.js</code> to write files outside the uploads folder.</p>
<h2>Storing Metadata</h2>
<p>Usually, you'll want to store information about the uploaded file:</p>
<pre><code class="language-javascript">import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.use(express.json());

const storage = multer.diskStorage({
  destination: (req, file, cb) =&gt; {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) =&gt; {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({ storage });

app.use('/files', express.static(path.join(__dirname, 'uploads')));

app.post('/upload', upload.single('file'), (req, res) =&gt; {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  
  // Store metadata about the file
  const fileData = {
    originalName: req.file.originalname,
    savedName: req.file.filename,
    size: req.file.size,
    mimetype: req.file.mimetype,
    uploadedAt: new Date(),
    url: `/files/${req.file.filename}`
  };
  
  // You'd normally save fileData to a database
  res.json(fileData);
});

app.listen(3000, () =&gt; console.log('Server running'));
</code></pre>
<p>Store this information in a database so you can track what was uploaded, when, and by whom.</p>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Using original filenames</strong></p>
<p>Never save files with their original names. Attackers can upload files with special characters or path traversal sequences.</p>
<p><strong>Mistake 2: Not validating file types</strong></p>
<p>Don't just check the file extension. Someone can rename <code>malware.exe</code> to <code>image.jpg</code>. Always check the MIME type.</p>
<p><strong>Mistake 3: Storing sensitive files in the web-accessible folder</strong></p>
<p>Files served via express.static are publicly accessible. Don't store passwords, API keys, or sensitive data there.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>Multer middleware handles file uploads in Express</p>
</li>
<li><p>Files are stored to disk in a designated folder</p>
</li>
<li><p>Use express.static middleware to serve uploaded files</p>
</li>
<li><p>Generate unique filenames to prevent security issues</p>
</li>
<li><p>Validate file types and sizes</p>
</li>
<li><p>Store file metadata in a database</p>
</li>
<li><p>Never trust the original filename provided by users</p>
</li>
</ul>
<p>Once you're comfortable with local storage, you can explore cloud storage solutions for better scalability and reliability.</p>
]]></content:encoded></item><item><title><![CDATA[URL Parameters vs Query Strings in Express.js]]></title><description><![CDATA[Your API needs to receive data from users. Express lets you pass data through the URL in two different ways: URL parameters and query strings. They look different, work differently, and have different]]></description><link>https://blog.ashish.pro/url-parameters-vs-query-strings-expressjs</link><guid isPermaLink="true">https://blog.ashish.pro/url-parameters-vs-query-strings-expressjs</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Wed, 29 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/c6e5cf7e-bb18-41ce-9c38-6f97010ee980.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your API needs to receive data from users. Express lets you pass data through the URL in two different ways: URL parameters and query strings. They look different, work differently, and have different purposes.</p>
<h2>URL Parameters: Part of the Route</h2>
<p>URL parameters are placeholders in the route path. They're required and define the resource you're accessing.</p>
<p>Consider these examples:</p>
<pre><code class="language-plaintext">/users/123          - get user with ID 123
/posts/456/comments - get comments for post 456
/products/789/reviews - get reviews for product 789
</code></pre>
<p>The numbers and identifiers are URL parameters. They identify which specific resource you want.</p>
<p>In Express, you define parameters with a colon:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/users/:id', (req, res) =&gt; {
  const userId = req.params.id;
  res.json({ message: `Getting user ${userId}` });
});

app.get('/posts/:postId/comments/:commentId', (req, res) =&gt; {
  const postId = req.params.postId;
  const commentId = req.params.commentId;
  res.json({ postId, commentId });
});

app.listen(3000);
</code></pre>
<p>When a request comes in, Express extracts the parameter values and places them in <code>req.params</code>.</p>
<p><strong>Usage examples:</strong></p>
<ul>
<li><p><code>GET /users/123</code> → <code>req.params.id</code> = <code>"123"</code></p>
</li>
<li><p><code>GET /posts/456/comments/789</code> → <code>req.params.postId</code> = <code>"456"</code>, <code>req.params.commentId</code> = <code>"789"</code></p>
</li>
</ul>
<h2>Query Strings: Optional Filters and Options</h2>
<p>Query strings come after a question mark in the URL. They're optional and add filtering or modification options.</p>
<p>Consider these examples:</p>
<pre><code class="language-plaintext">/users?role=admin           - filter users by role
/products?sort=price&amp;limit=10 - sort by price, limit to 10 results
/search?q=node&amp;lang=en      - search for "node" in English
</code></pre>
<p>In Express, query strings are available in <code>req.query</code>:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/users', (req, res) =&gt; {
  const role = req.query.role;
  const status = req.query.status;
  res.json({ message: `Getting users`, role, status });
});

app.get('/search', (req, res) =&gt; {
  const q = req.query.q;
  const limit = req.query.limit;
  res.json({ search: q, limit });
});

app.listen(3000);
</code></pre>
<p><strong>Usage examples:</strong></p>
<ul>
<li><p><code>GET /users?role=admin</code> → <code>req.query.role</code> = <code>"admin"</code></p>
</li>
<li><p><code>GET /search?q=javascript&amp;limit=20</code> → <code>req.query.q</code> = <code>"javascript"</code>, <code>req.query.limit</code> = <code>"20"</code></p>
</li>
</ul>
<h2>Parameters vs Query Strings: Key Differences</h2>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>URL Parameters</th>
<th>Query Strings</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Purpose</strong></td>
<td>Identify resource</td>
<td>Filter/modify results</td>
</tr>
<tr>
<td><strong>Required</strong></td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><strong>Syntax</strong></td>
<td><code>/users/123</code></td>
<td><code>/users?role=admin</code></td>
</tr>
<tr>
<td><strong>Express</strong></td>
<td><code>req.params</code></td>
<td><code>req.query</code></td>
</tr>
<tr>
<td><strong>Example</strong></td>
<td>Get specific user</td>
<td>Get users matching criteria</td>
</tr>
<tr>
<td><strong>Multiple values</strong></td>
<td>Hard to add</td>
<td>Easy with <code>&amp;</code></td>
</tr>
</tbody></table>
<h2>Real-World Examples</h2>
<p><strong>URL Parameters:</strong> Identify the exact resource you want</p>
<pre><code class="language-javascript">app.get('/users/:id', (req, res) =&gt; {
  // GET /users/123
  // Retrieve THE user with ID 123
});

app.get('/posts/:postId/comments/:commentId', (req, res) =&gt; {
  // GET /posts/456/comments/789
  // Get specific comment 789 in specific post 456
});
</code></pre>
<p><strong>Query Strings:</strong> Filter or modify what you're retrieving</p>
<pre><code class="language-javascript">app.get('/users', (req, res) =&gt; {
  // GET /users?role=admin&amp;status=active
  // Get all users, but filter by role and status
});

app.get('/posts', (req, res) =&gt; {
  // GET /posts?sort=date&amp;limit=10&amp;skip=20
  // Get posts, sorted by date, 10 per page, starting from position 20
});
</code></pre>
<h2>Combining Both</h2>
<p>You can use both parameters and query strings together:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/users/:id/posts', (req, res) =&gt; {
  const userId = req.params.id;
  const sort = req.query.sort;
  const limit = req.query.limit;
  
  res.json({
    message: `Getting posts for user ${userId}`,
    sort,
    limit
  });
});

app.listen(3000);
</code></pre>
<p>Examples:</p>
<ul>
<li><p><code>GET /users/123/posts</code> → User 123's posts</p>
</li>
<li><p><code>GET /users/123/posts?sort=date</code> → User 123's posts sorted by date</p>
</li>
<li><p><code>GET /users/123/posts?sort=date&amp;limit=10</code> → User 123's 10 most recent posts</p>
</li>
</ul>
<h2>Multiple Query Parameters</h2>
<p>When you have multiple query parameters, separate them with <code>&amp;</code>:</p>
<pre><code class="language-javascript">app.get('/search', (req, res) =&gt; {
  const query = req.query.q;
  const lang = req.query.lang;
  const category = req.query.category;
  
  res.json({ query, lang, category });
});
</code></pre>
<p><strong>Usage:</strong></p>
<pre><code class="language-plaintext">GET /search?q=javascript&amp;lang=en&amp;category=tutorial
→ query = "javascript", lang = "en", category = "tutorial"
</code></pre>
<h2>Query Parameters with Arrays</h2>
<p>You can pass multiple values for the same parameter:</p>
<pre><code class="language-javascript">app.get('/products', (req, res) =&gt; {
  const colors = req.query.color;
  res.json({ colors });
});
</code></pre>
<p><strong>Usage:</strong></p>
<pre><code class="language-plaintext">GET /products?color=red&amp;color=blue&amp;color=green
→ colors = ["red", "blue", "green"]
</code></pre>
<h2>Type Conversion</h2>
<p>Query parameters always come as strings. If you need numbers, convert them:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/products', (req, res) =&gt; {
  const limit = parseInt(req.query.limit) || 10;
  const skip = parseInt(req.query.skip) || 0;
  
  res.json({ limit, skip });
});

app.listen(3000);
</code></pre>
<p>Without conversion, <code>req.query.limit</code> is the string <code>"20"</code>, not the number <code>20</code>.</p>
<h2>When to Use Each</h2>
<p><strong>Use URL parameters when:</strong></p>
<ul>
<li><p>Identifying a specific resource</p>
</li>
<li><p>The value is required</p>
</li>
<li><p>Following REST principles for resource identification</p>
</li>
<li><p>Examples: user ID, post ID, product ID</p>
</li>
</ul>
<p><strong>Use query strings when:</strong></p>
<ul>
<li><p>Filtering results</p>
</li>
<li><p>Sorting or paginating</p>
</li>
<li><p>The value is optional</p>
</li>
<li><p>Adding modifiers or options</p>
</li>
<li><p>Examples: role, category, sort order, page number</p>
</li>
</ul>
<h2>REST API Best Practices</h2>
<p>A well-designed REST API uses both appropriately:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Get all users (with optional filters)
app.get('/users', (req, res) =&gt; {
  const role = req.query.role;
  const status = req.query.status;
  // Retrieve users, optionally filtered by role and status
});

// Get specific user
app.get('/users/:id', (req, res) =&gt; {
  const userId = req.params.id;
  // Retrieve specific user by ID
});

// Get posts for a specific user (with optional sorting)
app.get('/users/:userId/posts', (req, res) =&gt; {
  const userId = req.params.userId;
  const sort = req.query.sort;
  // Retrieve posts for user, optionally sorted
});

// Get specific comment on specific post by specific user
app.get('/users/:userId/posts/:postId/comments/:commentId', (req, res) =&gt; {
  const { userId, postId, commentId } = req.params;
  // Retrieve specific comment
});

app.listen(3000);
</code></pre>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Using parameters when query strings make sense</strong></p>
<pre><code class="language-javascript">// Wrong - parameters for filters
app.get('/users/:role/:status', (req, res) =&gt; {
  // URL becomes /users/admin/active
  // Hard to add more filters
});

// Better - query strings for filters
app.get('/users', (req, res) =&gt; {
  // URL is /users?role=admin&amp;status=active
  // Easy to add/remove filters
});
</code></pre>
<p><strong>Mistake 2: Forgetting to convert query parameters to numbers</strong></p>
<pre><code class="language-javascript">app.get('/products', (req, res) =&gt; {
  const limit = req.query.limit; // This is a string!
  const results = db.getProducts().slice(0, limit);
  // limit as string might not slice correctly
});
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>URL parameters identify a specific resource and go in the route path</p>
</li>
<li><p>Query strings are optional filters and go after the question mark</p>
</li>
<li><p>Parameters are required and part of the route definition</p>
</li>
<li><p>Query strings are optional and make URLs flexible</p>
</li>
<li><p>Access parameters via <code>req.params</code> and query strings via <code>req.query</code></p>
</li>
<li><p>Combine both for powerful, flexible APIs</p>
</li>
<li><p>Use parameters for identification, query strings for filtering</p>
</li>
</ul>
<p>Mastering both gives you the tools to build flexible, standard REST APIs that users can intuitively understand.</p>
]]></content:encoded></item><item><title><![CDATA[What is Middleware in Express and How It Works]]></title><description><![CDATA[Middleware sits between incoming requests and your route handlers. It intercepts requests, does something with them, and either passes them along or stops them. Understanding middleware is key to buil]]></description><link>https://blog.ashish.pro/what-is-middleware-expressjs</link><guid isPermaLink="true">https://blog.ashish.pro/what-is-middleware-expressjs</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Mon, 27 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/d6e07ad4-b4cd-47c1-b4b8-5d727e1fd584.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Middleware sits between incoming requests and your route handlers. It intercepts requests, does something with them, and either passes them along or stops them. Understanding middleware is key to building Express applications.</p>
<h2>What Is Middleware?</h2>
<p>Middleware is a function that has access to the request and response objects. It can modify them, perform checks, or stop the request.</p>
<p>Think of middleware as checkpoints in an airport:</p>
<ul>
<li><p>Security check (middleware validates the request)</p>
</li>
<li><p>Baggage scan (middleware processes data)</p>
</li>
<li><p>Gate (final destination/route handler)</p>
</li>
</ul>
<p>Each checkpoint can allow you through to the next, or stop you.</p>
<p>In Express, middleware functions have this signature:</p>
<pre><code class="language-javascript">function middleware(req, res, next) {
  // Do something
  next(); // Pass to next middleware
}
</code></pre>
<p>The <code>next()</code> function passes control to the next middleware or route handler.</p>
<h2>How Middleware Works</h2>
<p>When a request arrives, Express processes it through middleware in order:</p>
<pre><code class="language-plaintext">Request comes in
     |
     v
Middleware 1 (if next() called, continue)
     |
     v
Middleware 2 (if next() called, continue)
     |
     v
Route Handler (your actual route)
     |
     v
Response sent
</code></pre>
<p>Each middleware either calls <code>next()</code> to continue or sends a response to stop.</p>
<h2>Using Express-Provided Middleware</h2>
<p>Express provides built-in middleware for common tasks:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Parse JSON requests
app.use(express.json());

// Parse URL-encoded form data
app.use(express.urlencoded({ extended: true }));

// Serve static files
app.use(express.static('public'));

app.post('/data', (req, res) =&gt; {
  // Thanks to express.json() middleware, req.body is parsed
  console.log(req.body);
  res.send('Data received');
});

app.listen(3000);
</code></pre>
<p><code>express.json()</code> middleware automatically parses incoming JSON and populates <code>req.body</code>.</p>
<h2>Creating Custom Middleware</h2>
<p>Write your own middleware for custom logic:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Custom logging middleware
function logger(req, res, next) {
  console.log(`\({req.method} \){req.url}`);
  next(); // Pass to next middleware
}

app.use(logger);

app.get('/', (req, res) =&gt; {
  res.send('Home page');
});

app.listen(3000);
</code></pre>
<p>Every request is logged, then passed to the route handler.</p>
<p>Output for <code>GET /</code>:</p>
<pre><code class="language-plaintext">GET /
</code></pre>
<h2>Middleware Execution Order</h2>
<p>Middleware runs in the order you define it:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.use((req, res, next) =&gt; {
  console.log('1: First middleware');
  next();
});

app.use((req, res, next) =&gt; {
  console.log('2: Second middleware');
  next();
});

app.get('/', (req, res) =&gt; {
  console.log('3: Route handler');
  res.send('Done');
});

app.listen(3000);
</code></pre>
<p>For a <code>GET /</code> request, output is:</p>
<pre><code class="language-plaintext">1: First middleware
2: Second middleware
3: Route handler
</code></pre>
<p>Order matters. Middleware defined first runs first.</p>
<h2>The <code>next()</code> Function</h2>
<p>Calling <code>next()</code> passes control to the next function in the chain. Not calling it stops execution:</p>
<pre><code class="language-javascript">app.use((req, res, next) =&gt; {
  console.log('Middleware 1');
  next(); // Continue
});

app.use((req, res, next) =&gt; {
  console.log('Middleware 2');
  // Not calling next() - stops here
});

app.get('/', (req, res) =&gt; {
  console.log('Route'); // Never reached
  res.send('Hi');
});
</code></pre>
<p>This stops at "Middleware 2". The route handler is never called.</p>
<h2>Types of Middleware</h2>
<h3>Application-Level Middleware</h3>
<p>Runs for all routes or specific routes:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Runs for ALL routes
app.use((req, res, next) =&gt; {
  console.log('Global middleware');
  next();
});

// Runs only for /admin routes
app.use('/admin', (req, res, next) =&gt; {
  console.log('Admin middleware');
  next();
});

app.get('/', (req, res) =&gt; res.send('Home'));
app.get('/admin/users', (req, res) =&gt; res.send('Admin page'));

app.listen(3000);
</code></pre>
<h3>Router-Level Middleware</h3>
<p>Middleware on a specific router:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();
const router = express.Router();

router.use((req, res, next) =&gt; {
  console.log('Router middleware');
  next();
});

router.get('/users', (req, res) =&gt; res.send('Users'));

app.use('/api', router);
app.listen(3000);
</code></pre>
<h3>Built-In Middleware</h3>
<p>Express provides standard middleware:</p>
<pre><code class="language-javascript">app.use(express.json());
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
</code></pre>
<h3>Third-Party Middleware</h3>
<p>Use packages from npm:</p>
<pre><code class="language-bash">npm install cors
</code></pre>
<pre><code class="language-javascript">import cors from 'cors';

app.use(cors()); // Enable CORS
</code></pre>
<h2>Real-World Middleware Examples</h2>
<h3>Authentication Middleware</h3>
<pre><code class="language-javascript">function authenticateToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).send('No token');
  }

  // Verify token (simplified)
  if (token === 'valid-token') {
    req.user = { id: 1 };
    next();
  } else {
    res.status(403).send('Invalid token');
  }
}

app.get('/protected', authenticateToken, (req, res) =&gt; {
  res.send(`Hello user ${req.user.id}`);
});
</code></pre>
<h3>Error Handling Middleware</h3>
<pre><code class="language-javascript">app.get('/data', (req, res, next) =&gt; {
  try {
    throw new Error('Something went wrong');
  } catch (error) {
    next(error); // Pass to error handler
  }
});

// Error handling middleware (must have 4 parameters)
app.use((err, req, res, next) =&gt; {
  console.error(err.message);
  res.status(500).send('Server error');
});
</code></pre>
<h3>Request Timing Middleware</h3>
<pre><code class="language-javascript">app.use((req, res, next) =&gt; {
  const start = Date.now();

  res.on('finish', () =&gt; {
    const duration = Date.now() - start;
    console.log(`\({req.method} \){req.url} - ${duration}ms`);
  });

  next();
});
</code></pre>
<h2>Middleware with Parameters</h2>
<p>Middleware factories return middleware functions:</p>
<pre><code class="language-javascript">function logMessages(format) {
  return (req, res, next) =&gt; {
    if (format === 'simple') {
      console.log(`\({req.method} \){req.url}`);
    } else if (format === 'detailed') {
      console.log(`\({req.method} \){req.url} - ${req.headers['user-agent']}`);
    }
    next();
  };
}

app.use(logMessages('simple'));
// or
app.use(logMessages('detailed'));
</code></pre>
<h2>Complete Example with Multiple Middleware</h2>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Built-in middleware
app.use(express.json());

// Custom logging middleware
app.use((req, res, next) =&gt; {
  console.log(`[\({new Date().toISOString()}] \){req.method} ${req.url}`);
  next();
});

// Authentication middleware
function authenticate(req, res, next) {
  const token = req.query.token;
  if (token === 'secret') {
    req.user = { id: 1, name: 'John' };
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
}

// Request validation middleware
function validateRequest(req, res, next) {
  if (!req.body.email) {
    return res.status(400).send('Email required');
  }
  next();
}

// Public route
app.get('/home', (req, res) =&gt; {
  res.send('Welcome');
});

// Protected route
app.get('/profile', authenticate, (req, res) =&gt; {
  res.send(`Hello ${req.user.name}`);
});

// Route with validation
app.post('/register', validateRequest, (req, res) =&gt; {
  res.send(`Registered ${req.body.email}`);
});

// Error handling middleware (must be last)
app.use((err, req, res, next) =&gt; {
  console.error(err);
  res.status(500).json({ error: 'Server error' });
});

app.listen(3000, () =&gt; {
  console.log('Server running');
});
</code></pre>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Forgetting to call</strong> <code>next()</code></p>
<pre><code class="language-javascript">// Wrong - middleware stops here
app.use((req, res, next) =&gt; {
  console.log('Middleware');
  // No next() call - route never executes
});

app.get('/', (req, res) =&gt; {
  res.send('Hello'); // Never reached
});

// Right
app.use((req, res, next) =&gt; {
  console.log('Middleware');
  next(); // Passes to next handler
});
</code></pre>
<p><strong>Mistake 2: Wrong middleware order</strong></p>
<pre><code class="language-javascript">// Wrong - middleware after route can't help
app.get('/', (req, res) =&gt; {
  res.send(req.body); // undefined - JSON not parsed yet
});

app.use(express.json()); // Too late!

// Right - middleware before routes
app.use(express.json());
app.get('/', (req, res) =&gt; {
  res.send(req.body); // Properly parsed
});
</code></pre>
<p><strong>Mistake 3: Error handler must have 4 parameters</strong></p>
<pre><code class="language-javascript">// Wrong - Express won't recognize as error handler
app.use((err, req, res) =&gt; {
  res.send('Error');
});

// Right - all 4 parameters required
app.use((err, req, res, next) =&gt; {
  res.send('Error');
});
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>Middleware intercepts requests and can modify them or stop them</p>
</li>
<li><p>Middleware runs in the order it's defined</p>
</li>
<li><p>Call <code>next()</code> to pass to the next middleware</p>
</li>
<li><p>Express provides built-in middleware like <code>express.json()</code></p>
</li>
<li><p>You can create custom middleware for any task</p>
</li>
<li><p>Application-level middleware runs for all/specific routes</p>
</li>
<li><p>Error handlers are middleware with 4 parameters</p>
</li>
<li><p>Order matters: define middleware before routes that need it</p>
</li>
</ul>
<p>Middleware is the backbone of Express applications. Master it, and you can build clean, modular, and powerful web applications.</p>
]]></content:encoded></item><item><title><![CDATA[Creating Routes and Handling Requests with Express]]></title><description><![CDATA[Node.js has a built-in HTTP module, but writing servers with it is verbose. Express.js simplifies everything. It handles routing, request parsing, and response sending with clean, simple code.
What is]]></description><link>https://blog.ashish.pro/creating-routes-handling-requests-express</link><guid isPermaLink="true">https://blog.ashish.pro/creating-routes-handling-requests-express</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Sat, 25 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/22bfdb22-8e1f-413e-8b76-a596976c0f51.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Node.js has a built-in HTTP module, but writing servers with it is verbose. Express.js simplifies everything. It handles routing, request parsing, and response sending with clean, simple code.</p>
<h2>What is Express?</h2>
<p>Express is a lightweight web framework for Node.js. It makes building web applications easier by providing helpful features for routing, middleware, and responses.</p>
<p>Without Express, here's a basic Node.js server:</p>
<pre><code class="language-javascript">import http from 'http';

const server = http.createServer((req, res) =&gt; {
  if (req.url === '/' &amp;&amp; req.method === 'GET') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Home page');
  } else if (req.url === '/about' &amp;&amp; req.method === 'GET') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('About page');
  } else {
    res.statusCode = 404;
    res.end('Not found');
  }
});

server.listen(3000);
</code></pre>
<p>This gets messy quickly. With multiple routes, you'd have many if-else statements.</p>
<p>With Express, the same thing is much simpler:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/', (req, res) =&gt; {
  res.send('Home page');
});

app.get('/about', (req, res) =&gt; {
  res.send('About page');
});

app.listen(3000);
</code></pre>
<p>Much cleaner. Express handles the complexity.</p>
<h2>Setting Up Express</h2>
<p>First, create a project and install Express:</p>
<pre><code class="language-bash">npm init -y
npm install express
</code></pre>
<p>Create a file called <code>server.js</code>:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.listen(3000, () =&gt; {
  console.log('Server running on http://localhost:3000');
});
</code></pre>
<p>Run it:</p>
<pre><code class="language-bash">node server.js
</code></pre>
<p>You have a running server. It doesn't do anything yet, but it's listening on port 3000.</p>
<h2>Understanding Routes</h2>
<p>A route is a combination of:</p>
<ul>
<li><p><strong>HTTP method</strong> (GET, POST, PUT, DELETE, etc.)</p>
</li>
<li><p><strong>URL path</strong> (/home, /users, /posts/123, etc.)</p>
</li>
<li><p><strong>Handler function</strong> (what to do when this route is called)</p>
</li>
</ul>
<p>The basic structure is:</p>
<pre><code class="language-javascript">app.METHOD(PATH, HANDLER);
</code></pre>
<p>For example:</p>
<pre><code class="language-javascript">app.get('/home', (req, res) =&gt; {
  res.send('This is the home page');
});
</code></pre>
<p>This means: "When someone sends a GET request to /home, execute this function."</p>
<h2>GET Requests</h2>
<p>GET requests fetch data. They're read-only and don't change anything.</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/', (req, res) =&gt; {
  res.send('Welcome to the home page');
});

app.get('/about', (req, res) =&gt; {
  res.send('This is the about page');
});

app.get('/contact', (req, res) =&gt; {
  res.send('Contact us at info@example.com');
});

app.listen(3000, () =&gt; {
  console.log('Server running');
});
</code></pre>
<p>Now visit:</p>
<ul>
<li><p><code>http://localhost:3000/</code> → "Welcome to the home page"</p>
</li>
<li><p><code>http://localhost:3000/about</code> → "This is the about page"</p>
</li>
<li><p><code>http://localhost:3000/contact</code> → "Contact us at..."</p>
</li>
</ul>
<h2>POST Requests</h2>
<p>POST requests send data to the server. They typically create new resources.</p>
<p>For this, you need to parse incoming data. Express provides middleware for this:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Parse JSON data
app.use(express.json());

app.get('/', (req, res) =&gt; {
  res.send('GET request received');
});

app.post('/submit', (req, res) =&gt; {
  const { name, email } = req.body;
  res.send(`Thank you, \({name}. We'll email \){email}`);
});

app.listen(3000, () =&gt; {
  console.log('Server running');
});
</code></pre>
<p>When someone sends a POST request to <code>/submit</code> with JSON data, Express parses it and stores it in <code>req.body</code>.</p>
<p>To test this, you'd need a tool like Postman or curl:</p>
<pre><code class="language-bash">curl -X POST http://localhost:3000/submit \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"john@example.com"}'
</code></pre>
<p>Response: "Thank you, John. We'll email <a href="mailto:john@example.com">john@example.com</a>"</p>
<h2>URL Parameters</h2>
<p>Routes can have dynamic parts. Parameters start with a colon:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/users/:id', (req, res) =&gt; {
  const userId = req.params.id;
  res.send(`Getting user ${userId}`);
});

app.get('/posts/:postId/comments/:commentId', (req, res) =&gt; {
  const { postId, commentId } = req.params;
  res.send(`Comment \({commentId} on post \){postId}`);
});

app.listen(3000);
</code></pre>
<p>Examples:</p>
<ul>
<li><p><code>GET /users/123</code> → "Getting user 123"</p>
</li>
<li><p><code>GET /posts/456/comments/789</code> → "Comment 789 on post 456"</p>
</li>
</ul>
<h2>Query Parameters</h2>
<p>Query parameters come after a question mark and are optional:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/search', (req, res) =&gt; {
  const query = req.query.q;
  const limit = req.query.limit;
  res.send(`Searching for "\({query}", limit \){limit}`);
});

app.listen(3000);
</code></pre>
<p>Examples:</p>
<ul>
<li><p><code>GET /search?q=javascript</code> → "Searching for 'javascript', limit undefined"</p>
</li>
<li><p><code>GET /search?q=node&amp;limit=10</code> → "Searching for 'node', limit 10"</p>
</li>
</ul>
<h2>Response Methods</h2>
<p>Express provides several ways to send responses:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

// Send plain text
app.get('/text', (req, res) =&gt; {
  res.send('This is plain text');
});

// Send JSON
app.get('/json', (req, res) =&gt; {
  res.json({ message: 'This is JSON', status: 'success' });
});

// Send HTML
app.get('/html', (req, res) =&gt; {
  res.send('&lt;h1&gt;This is HTML&lt;/h1&gt;');
});

// Send with status code
app.get('/error', (req, res) =&gt; {
  res.status(404).send('Not found');
});

// Redirect
app.get('/old-path', (req, res) =&gt; {
  res.redirect('/new-path');
});

app.listen(3000);
</code></pre>
<h2>Handling All HTTP Methods</h2>
<p>Express supports all standard HTTP methods:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.use(express.json());

app.get('/users', (req, res) =&gt; {
  res.json({ message: 'Get all users' });
});

app.post('/users', (req, res) =&gt; {
  res.json({ message: 'Create new user' });
});

app.put('/users/:id', (req, res) =&gt; {
  res.json({ message: `Update user ${req.params.id}` });
});

app.delete('/users/:id', (req, res) =&gt; {
  res.json({ message: `Delete user ${req.params.id}` });
});

app.listen(3000);
</code></pre>
<h2>Handling 404 Errors</h2>
<p>When a route doesn't exist, Express returns a 404 error by default. You can customize this:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();

app.get('/', (req, res) =&gt; {
  res.send('Home');
});

// Catch all unmatched routes
app.use((req, res) =&gt; {
  res.status(404).send('Page not found');
});

app.listen(3000);
</code></pre>
<p>The order matters. This "catch-all" route must be last, otherwise it will intercept all requests.</p>
<h2>A Complete Example</h2>
<p>Let's build a simple blog API:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();
app.use(express.json());

// Sample data
let posts = [
  { id: 1, title: 'First Post', content: 'Hello world' },
  { id: 2, title: 'Second Post', content: 'Another post' }
];

// GET all posts
app.get('/posts', (req, res) =&gt; {
  res.json(posts);
});

// GET single post
app.get('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).send('Post not found');
  }
  res.json(post);
});

// CREATE new post
app.post('/posts', (req, res) =&gt; {
  const newPost = {
    id: posts.length + 1,
    title: req.body.title,
    content: req.body.content
  };
  posts.push(newPost);
  res.status(201).json(newPost);
});

// UPDATE post
app.put('/posts/:id', (req, res) =&gt; {
  const post = posts.find(p =&gt; p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).send('Post not found');
  }
  post.title = req.body.title || post.title;
  post.content = req.body.content || post.content;
  res.json(post);
});

// DELETE post
app.delete('/posts/:id', (req, res) =&gt; {
  const index = posts.findIndex(p =&gt; p.id === parseInt(req.params.id));
  if (index === -1) {
    return res.status(404).send('Post not found');
  }
  const deleted = posts.splice(index, 1);
  res.json(deleted);
});

app.listen(3000, () =&gt; {
  console.log('Blog API running on port 3000');
});
</code></pre>
<p>This simple API lets you create, read, update, and delete posts.</p>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Forgetting middleware</strong></p>
<pre><code class="language-javascript">// Wrong - won't parse JSON
app.post('/data', (req, res) =&gt; {
  console.log(req.body); // undefined
});

// Correct
app.use(express.json());
app.post('/data', (req, res) =&gt; {
  console.log(req.body); // has data
});
</code></pre>
<p><strong>Mistake 2: Forgetting to return after sending response</strong></p>
<pre><code class="language-javascript">app.get('/users/:id', (req, res) =&gt; {
  if (!id) {
    res.status(400).send('Invalid ID');
    // BUG: continues executing below
  }
  res.send('User data');
});

// Better
app.get('/users/:id', (req, res) =&gt; {
  if (!id) {
    return res.status(400).send('Invalid ID');
  }
  res.send('User data');
});
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>Express simplifies building web servers with Node.js</p>
</li>
<li><p>Routes combine HTTP method, path, and handler function</p>
</li>
<li><p>GET requests fetch data, POST requests send data</p>
</li>
<li><p>URL parameters (<code>:id</code>) capture dynamic values</p>
</li>
<li><p>Query parameters (<code>?key=value</code>) pass optional filters</p>
</li>
<li><p>Middleware processes requests before they reach handlers</p>
</li>
<li><p>Always return after sending a response</p>
</li>
<li><p>Use appropriate HTTP status codes</p>
</li>
</ul>
<p>Express makes building web applications enjoyable. These fundamentals are the foundation for all Express applications.</p>
]]></content:encoded></item><item><title><![CDATA[Why Node.js is Perfect for Building Fast Web Applications]]></title><description><![CDATA[Your web application needs to handle thousands of concurrent users. It needs to be responsive. It needs to scale without requiring a massive infrastructure. Node.js is built for exactly this.
What Mak]]></description><link>https://blog.ashish.pro/why-nodejs-is-perfect-fast-web-apps</link><guid isPermaLink="true">https://blog.ashish.pro/why-nodejs-is-perfect-fast-web-apps</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Thu, 23 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/288ff549-1306-4aa5-a887-4bd5d98faf3c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your web application needs to handle thousands of concurrent users. It needs to be responsive. It needs to scale without requiring a massive infrastructure. Node.js is built for exactly this.</p>
<h2>What Makes Node.js Fast?</h2>
<p>There are two reasons Node.js excels at web applications: non-blocking I/O and an event-driven architecture.</p>
<p><strong>Non-blocking I/O</strong> means your application doesn't freeze while waiting for databases or file systems. Operations like database queries are delegated, and your code continues.</p>
<p><strong>Event-driven</strong> means your application responds to events. A database completes? Event. A file loads? Event. Request arrives? Event. Your code reacts to these events.</p>
<p>Together, these features make Node.js incredibly efficient.</p>
<h2>The Blocking Problem</h2>
<p>Traditional servers block while waiting for operations:</p>
<pre><code class="language-javascript">// Blocking server (pseudo-code, not Node.js)
request1 arrives
  database query takes 2 seconds
  (server frozen, can't handle other requests)
response sent to request1

request2 can now be handled
  database query takes 2 seconds
  (server frozen)
response sent to request2
</code></pre>
<p>With 100 requests each waiting 2 seconds, the total time is 200 seconds. Users wait a long time.</p>
<h2>Node.js: Non-Blocking</h2>
<p>Node.js handles the same scenario differently:</p>
<pre><code class="language-javascript">request1 arrives
  database query delegated
  (server free immediately)

request2 arrives
  database query delegated
  (server free immediately)

database completes for request1
  response sent

database completes for request2
  response sent
</code></pre>
<p>Total time is 2 seconds. Users get responses quickly.</p>
<h2>Event Loop: The Heart of Node.js Performance</h2>
<p>The event loop is what makes this possible. It continuously checks for completed operations and executes their callbacks:</p>
<pre><code class="language-plaintext">Main Thread (JavaScript)
     |
Callbacks Queue &lt;- Operations complete (databases, files, timers)
     |
Event Loop (running continuously)
  1. Is there a callback ready?
  2. Execute it
  3. Go back to step 1
</code></pre>
<p>Here's a concrete example:</p>
<pre><code class="language-javascript">import http from 'http';
import { readFile } from 'fs';

http.createServer((req, res) =&gt; {
  // Delegate file read
  readFile('data.txt', (err, data) =&gt; {
    res.end(data);
  });
  // Main thread immediately available for next request
}).listen(3000);
</code></pre>
<p>When the file is read, the callback executes and sends the response. The main thread isn't blocked.</p>
<h2>Comparison: Blocking vs Non-Blocking</h2>
<p>Imagine two servers, one blocking, one non-blocking, handling three requests that each take 1 second:</p>
<p><strong>Blocking Server:</strong></p>
<pre><code class="language-plaintext">Time 0: Request 1 arrives, processing starts
Time 1: Request 1 done, Response 1 sent
Time 1: Request 2 arrives, processing starts
Time 2: Request 2 done, Response 2 sent
Time 2: Request 3 arrives, processing starts
Time 3: Request 3 done, Response 3 sent
</code></pre>
<p>Total time: 3 seconds</p>
<p><strong>Non-Blocking (Node.js):</strong></p>
<pre><code class="language-plaintext">Time 0: Request 1 arrives, work delegated
Time 0: Request 2 arrives, work delegated
Time 0: Request 3 arrives, work delegated
Time 1: All three operations complete, responses sent
</code></pre>
<p>Total time: 1 second</p>
<p>Node.js is 3x faster for concurrent requests.</p>
<h2>Where Node.js Shines</h2>
<p><strong>Database Queries:</strong></p>
<pre><code class="language-javascript">// While database is querying, Node.js handles other requests
db.query('SELECT * FROM users', (err, results) =&gt; {
  res.json(results);
});
</code></pre>
<p><strong>File Operations:</strong></p>
<pre><code class="language-javascript">// While file is being read, handle other requests
fs.readFile('large-file.txt', (err, data) =&gt; {
  res.send(data);
});
</code></pre>
<p><strong>API Calls:</strong></p>
<pre><code class="language-javascript">// While waiting for external API, handle other requests
fetch('https://api.example.com/data')
  .then(res =&gt; res.json())
  .then(data =&gt; sendResponse(data));
</code></pre>
<p>Most web applications spend most time waiting for these operations. Node.js excels because it doesn't block during waits.</p>
<h2>Real-World Performance: Restaurant Analogy</h2>
<p>Think of a web server as a restaurant:</p>
<p><strong>Blocking server:</strong> The only waiter must take an order, go to the kitchen, wait for the food to cook, serve it, before taking another order. Customer 2 waits while Customer 1's food cooks.</p>
<p><strong>Node.js server:</strong> The waiter takes orders from multiple customers, gives them all to the kitchen, and serves them as they're ready. While the kitchen cooks, the waiter takes more orders.</p>
<p>The kitchen is background workers (databases, file systems). The waiter is the Node.js thread.</p>
<h2>Scalability Without Massive Infrastructure</h2>
<p>Because Node.js doesn't create a thread per request, it uses minimal resources:</p>
<ul>
<li><p>A traditional server with 10,000 concurrent users might need 10,000 threads</p>
</li>
<li><p>Node.js handles 10,000 concurrent connections with one thread plus a thread pool</p>
</li>
</ul>
<p>Memory usage is drastically lower. You don't need an expensive server farm.</p>
<h2>When Node.js Isn't the Best Choice</h2>
<p>Node.js is not ideal for CPU-intensive tasks:</p>
<pre><code class="language-javascript">// Bad for Node.js - heavy computation blocks the thread
app.get('/calculate', (req, res) =&gt; {
  let result = 0;
  for (let i = 0; i &lt; 1_000_000_000; i++) {
    result += i;
  }
  res.send(result);
});
</code></pre>
<p>While calculating, the thread is blocked, and other requests wait.</p>
<p>For CPU-intensive tasks, use worker threads or other languages.</p>
<p>But for typical web applications where time is spent on I/O, Node.js is exceptional.</p>
<h2>Real-World Usage: Who Uses Node.js?</h2>
<p><strong>Netflix</strong>: Chose Node.js for its ability to handle millions of concurrent users streaming video.</p>
<p><strong>Uber</strong>: Real-time ride matching requires handling many concurrent connections. Node.js handles this perfectly.</p>
<p><strong>LinkedIn</strong>: Uses Node.js for features requiring real-time updates.</p>
<p><strong>Airbnb</strong>: Uses Node.js in its infrastructure for handling search and booking requests.</p>
<p>These companies didn't choose Node.js randomly. They chose it because it performs well at scale.</p>
<h2>Performance Metrics</h2>
<p>Here's what typical Node.js performance looks like:</p>
<ul>
<li><p><strong>Throughput</strong>: Handles thousands of requests per second on modest hardware</p>
</li>
<li><p><strong>Latency</strong>: Response times measured in milliseconds</p>
</li>
<li><p><strong>Memory</strong>: Lower memory footprint than traditional threaded servers</p>
</li>
<li><p><strong>Scalability</strong>: Scales horizontally by running multiple Node.js processes</p>
</li>
</ul>
<h2>Optimization Tips</h2>
<p>Even with Node.js, good practices matter:</p>
<p><strong>Use async/await:</strong></p>
<pre><code class="language-javascript">// Good - non-blocking
app.get('/data', async (req, res) =&gt; {
  const data = await db.query('SELECT * FROM users');
  res.json(data);
});
</code></pre>
<p><strong>Avoid synchronous operations:</strong></p>
<pre><code class="language-javascript">// Bad - blocks thread
const data = fs.readFileSync('large-file.txt');

// Good - non-blocking
fs.readFile('large-file.txt', (err, data) =&gt; {
  // Handle data
});
</code></pre>
<p><strong>Use caching:</strong></p>
<pre><code class="language-javascript">// Cache database results to avoid repeated queries
const cache = {};
app.get('/users', (req, res) =&gt; {
  if (cache.users) {
    return res.json(cache.users);
  }
  db.query('SELECT * FROM users', (err, results) =&gt; {
    cache.users = results;
    res.json(results);
  });
});
</code></pre>
<p><strong>Load balancing:</strong> Run multiple Node.js processes across multiple CPU cores:</p>
<pre><code class="language-javascript">import cluster from 'cluster';
import os from 'os';
import app from './app.js';

if (cluster.isPrimary) {
  const numCPUs = os.cpus().length;
  for (let i = 0; i &lt; numCPUs; i++) {
    cluster.fork();
  }
} else {
  app.listen(3000);
}
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>Node.js is fast because it's non-blocking and event-driven</p>
</li>
<li><p>While waiting for I/O, Node.js handles other requests</p>
</li>
<li><p>The event loop continuously checks for completed operations</p>
</li>
<li><p>Node.js handles thousands of concurrent connections efficiently</p>
</li>
<li><p>Traditional servers would need thousands of threads; Node.js uses one</p>
</li>
<li><p>Node.js excels for I/O-heavy applications (databases, files, APIs)</p>
</li>
<li><p>Node.js is not ideal for CPU-intensive tasks</p>
</li>
<li><p>Major companies like Netflix and Uber use Node.js at massive scale</p>
</li>
</ul>
<p>Node.js isn't fast just because it's JavaScript. It's fast because of its fundamentally different approach to concurrency. Understand non-blocking I/O and the event loop, and you understand why Node.js is perfect for modern web applications.</p>
]]></content:encoded></item><item><title><![CDATA[The Secret Life of the Linux Filesystem: What I Learned by Digging Around]]></title><description><![CDATA[When you first learn Linux, you naturally focus on commands. You memorize how to list files, move directories, and create new folders. But typing commands into a terminal is just scratching the surfac]]></description><link>https://blog.ashish.pro/linux-filesystem-deep-dive-under-the-hood</link><guid isPermaLink="true">https://blog.ashish.pro/linux-filesystem-deep-dive-under-the-hood</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Wed, 22 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/38141bda-736a-4ab7-b0be-6c813f9b29e0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you first learn Linux, you naturally focus on commands. You memorize how to list files, move directories, and create new folders. But typing commands into a terminal is just scratching the surface of what the operating system can do. To actually understand how Linux thinks, you have to look at the filesystem itself.</p>
<p>Linux follows a brilliant philosophical rule: "Everything is a file." Your hard drive is a file. Your keyboard translates to a file. The running processes taking up your RAM are represented as files. Network sockets are treated as files. This sounds like an abstract academic concept until you start exploring the directories and see how it works in practice.</p>
<p>By exploring the system directories, I stopped viewing Linux as a black box of random commands. Instead, I started seeing it as a beautifully organized machine where every configuration, process, and hardware component has a specific address. Here are nine fascinating discoveries from digging deep into the Linux filesystem.</p>
<h2>1. The Phantom Folder: /proc</h2>
<p>Normally, we think of files as data saved permanently on a physical hard drive spinning in your computer. But when you navigate to <code>/proc</code> and check its size, you will notice something strange. It takes up exactly zero bytes on your disk.</p>
<p><strong>What it does:</strong> The <code>/proc</code> directory is a virtual filesystem. It does not exist on your hard drive at all. It is generated continuously in your computer's RAM by the Linux kernel.</p>
<p><strong>Why it exists:</strong> Operating systems need a way for users and monitoring tools to check system health, memory usage, and CPU details. Instead of writing complex API tools to fetch this data, Linux just exposes it as text files.</p>
<p><strong>The problem it solves:</strong> It bridges the gap between kernel memory and user space. Without <code>/proc</code>, you would need special low-level programming skills just to check your RAM usage.</p>
<p><strong>The insight:</strong> If you type <code>cat /proc/cpuinfo</code>, you are not reading a saved document. You are actively querying the kernel for your processor's live hardware specs. Every running program on your computer gets its own numbered folder inside <code>/proc</code>. Those folders hold text files that describe the exact memory usage and execution state of that specific application.</p>
<h2>2. The Password Decoupling: /etc/passwd vs /etc/shadow</h2>
<p>User management feels like it should require a complex, encrypted database engine running in the background. In Linux, user management is handled entirely by a couple of plain text files.</p>
<p><strong>What it does:</strong> The <code>/etc/passwd</code> file lists every user and system account on the machine. The <code>/etc/shadow</code> file stores the actual hashed passwords.</p>
<p><strong>Why it exists:</strong> Applications need to know which users exist on a system to assign file permissions and ownership. They need a fast, simple way to look up user IDs.</p>
<p><strong>The problem it solves:</strong> Originally, early Unix systems stored hashed passwords directly inside <code>/etc/passwd</code>. The problem was that many basic programs need to read <code>/etc/passwd</code> to map user IDs to actual usernames. Since the file had to be readable by everyone, attackers could simply copy the text file and run brute-force password cracking tools on the hashes.</p>
<p><strong>The insight:</strong> To fix this massive security flaw without breaking existing software, engineers split the system in two. They left the usernames in the world-readable <code>/etc/passwd</code> file. Then they moved the password hashes into <code>/etc/shadow</code>. Only the root administrator has permission to read the shadow file. This simple structural split secured the entire operating system while maintaining backward compatibility.</p>
<h2>3. The Internet Translators: /etc/resolv.conf and /etc/hosts</h2>
<p>When you type a website name into your browser, your computer has no idea where that server actually lives. It needs to ask a Domain Name System (DNS) server for the specific IP address.</p>
<p><strong>What it does:</strong> The <code>/etc/resolv.conf</code> file is a text document that tells your computer exactly which DNS servers to ask for directions. Meanwhile, <code>/etc/hosts</code> provides manual, local overrides.</p>
<p><strong>Why it exists:</strong> Network settings change frequently depending on your location. By keeping the DNS configuration in predictably located text files, networking scripts and VPN clients can quickly update your routing preferences.</p>
<p><strong>The problem it solves:</strong> It standardizes how network name resolution is configured across almost all Unix-like systems. Before the system reaches out to the open internet, it checks <code>/etc/hosts</code> to see if you have hardcoded a specific IP address for a domain.</p>
<p><strong>The insight:</strong> Your machine's ability to browse the internet relies entirely on <code>/etc/resolv.conf</code>. If you delete it or put the wrong IP address inside, your computer will stay connected to the Wi-Fi network but will completely lose the ability to load web pages by their domain names. Furthermore, web developers often edit <code>/etc/hosts</code> to point legitimate domains to their local testing servers during development.</p>
<h2>4. The Digital Black Hole: /dev/null</h2>
<p>If you look inside the <code>/dev</code> directory, you will find files representing your mouse, your hard drives, and your camera. But you will also find a device file that goes nowhere.</p>
<p><strong>What it does:</strong> The <code>/dev/null</code> file is a special device file that instantly discards any data written to it.</p>
<p><strong>Why it exists:</strong> Data constantly moves through a Linux system. Sometimes you only care if a program ran successfully, and you do not care about the diagnostic text it spits out.</p>
<p><strong>The problem it solves:</strong> When you run automated tasks or background scripts, they often generate massive amounts of terminal output or warning messages. If you do not want to see these messages and do not want them taking up space in a log file, you need a safe way to throw them away.</p>
<p><strong>The insight:</strong> Because "everything is a file" in Linux, engineers created a file that acts as a trash can. When developers append <code>&gt; /dev/null</code> to a backup script command, they are redirecting the noisy output into this endless black hole. It accepts infinite data and never gets full.</p>
<h2>5. The Blueprint of the Hard Drives: /etc/fstab</h2>
<p>Booting up a computer is a fragile process. The system needs to know exactly which hard drives to connect and where to put them in the filesystem hierarchy.</p>
<p><strong>What it does:</strong> The File System Table, located at <code>/etc/fstab</code>, is the master map for your storage drives. It tells the Linux kernel exactly which partitions to mount and what rules to apply to them during startup.</p>
<p><strong>Why it exists:</strong> Linux allows you to mount a secondary hard drive into any empty folder you want. You could mount a massive storage drive directly to <code>/var/www/html</code> to store web server files.</p>
<p><strong>The problem it solves:</strong> The system needs a persistent record of these random hardware locations so it can rebuild the folder structure exactly the same way every time you restart.</p>
<p><strong>The insight:</strong> This file is incredibly powerful but equally dangerous. A simple typo in <code>/etc/fstab</code> can completely break the boot sequence. If the system cannot read the instruction manual for the hard drives, it drops your server into a locked-down recovery mode because it physically cannot piece together the filesystem.</p>
<h2>6. The Bouncer's Notepad: /var/log/auth.log</h2>
<p>Servers attached to the internet are constantly under attack. Bots scan the web trying to guess passwords and force their way into remote machines.</p>
<p><strong>What it does:</strong> The <code>/var/log/auth.log</code> file (which is sometimes called <code>secure</code> on certain distributions) acts as a relentless journal tracking every single authentication attempt on the system.</p>
<p><strong>Why it exists:</strong> You cannot improve security if you do not know you are being attacked. System logs provide visibility into exactly who is trying to access the machine.</p>
<p><strong>The problem it solves:</strong> Administrators need a way to audit security. If someone is trying to guess passwords or if an employee successfully logs in, the system requires an unalterable history of that event for forensic analysis.</p>
<p><strong>The insight:</strong> Reading this file on a live public server is eye-opening. You will see hundreds of automated attempts from random IP addresses trying to log in as "root" or "admin". Watching the authentication log populate in real time proves exactly why strong passwords and SSH keys are mandatory for remote servers.</p>
<h2>7. The Puppet Master of Services: /etc/systemd</h2>
<p>Applications like web servers and databases need to continually run in the background. They also need to automatically start back up if the server reboots.</p>
<p><strong>What it does:</strong> The <code>/etc/systemd</code> directory stores the configuration files that dictate how background services start, stop, and behave.</p>
<p><strong>Why it exists:</strong> A server might run fifty different background applications simultaneously. The operating system needs a standardized way to manage dependencies, like making sure the networking service starts before the web server tries to connect to the internet.</p>
<p><strong>The problem it solves:</strong> Before systemd, starting services required messy, non-standard bash scripts. Systemd introduced a clean, uniform configuration format (called unit files) that makes service management predictable.</p>
<p><strong>The insight:</strong> By reading a service file inside <code>/etc/systemd/system</code>, you can see exactly how an application launches. You can see which user account it runs under and what environment variables it requires. This directory is the central nervous system for keeping your server applications running smoothly.</p>
<h2>8. The Amnesiac Folders: /tmp and /var/tmp</h2>
<p>Programs constantly need to create temporary files. A video editor needs a place to store cached clips, and a web server needs a place to hold uploaded images before moving them to permanent storage.</p>
<p><strong>What it does:</strong> The <code>/tmp</code> directory is a global scratchpad available to any application or user. The <code>/var/tmp</code> directory serves a similar purpose but with a crucial difference in persistence.</p>
<p><strong>Why it exists:</strong> If applications threw temporary files all over your home directory, your disk would fill up with garbage in a week. Having dedicated temporary folders keeps the rest of the filesystem clean.</p>
<p><strong>The problem it solves:</strong> It provides a safe sandbox where applications write temporary data without worrying about cleaning it up perfectly.</p>
<p><strong>The insight:</strong> The Linux system usually wipes the entire <code>/tmp</code> directory clean every time you reboot the computer. It is a completely volatile storage area. However, files stored in <code>/var/tmp</code> are designed to survive a reboot. Understanding this difference is critical when writing backend scripts that process temporary data.</p>
<h2>9. The Infinity Generator: /dev/urandom</h2>
<p>Cryptographic security relies on random numbers. When your server generates an SSH key or an SSL certificate, it needs a source of pure unpredictability.</p>
<p><strong>What it does:</strong> The <code>/dev/urandom</code> file is a special device file that outputs an endless stream of random characters.</p>
<p><strong>Why it exists:</strong> Computers are deterministic machines. They follow instructions exactly. Because of this, generating actual randomness is incredibly difficult for a CPU.</p>
<p><strong>The problem it solves:</strong> Linux gathers "environmental noise" from device drivers, keyboard timings, and mouse movements to generate a pool of randomness. It then exposes this randomness through <code>/dev/urandom</code> so any application can securely generate encryption keys.</p>
<p><strong>The insight:</strong> You can actually read this file directly by running <code>cat /dev/urandom</code>, which will flood your terminal screen with gibberish. It is amazing to realize that complex cryptographic applications rely on reading a simple stream of characters from this exact device file.</p>
<h2>Final Thoughts</h2>
<p>The Linux filesystem is essentially the system's brain exposed as readable text. When you run a command, it is usually just a fancy wrapper that reads from or writes to one of these hidden configuration files.</p>
<p>By understanding where the configurations live and how the virtual filesystems operate, you stop being someone who just memorizes commands. You start becoming a developer who understands how the machine actually breathes at a core architectural level.</p>
]]></content:encoded></item><item><title><![CDATA[Async Code in Node.js: Callbacks and Promises]]></title><description><![CDATA[When you send a request to your Node.js server, you don't want it to freeze while waiting for a database to respond or a file to load. That's where asynchronous code comes in. It lets your server hand]]></description><link>https://blog.ashish.pro/async-code-nodejs-callbacks-promises</link><guid isPermaLink="true">https://blog.ashish.pro/async-code-nodejs-callbacks-promises</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Tue, 21 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/8b1b55b7-f263-456e-b371-07fe11bea229.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you send a request to your Node.js server, you don't want it to freeze while waiting for a database to respond or a file to load. That's where asynchronous code comes in. It lets your server handle other requests while waiting for operations to complete.</p>
<h2>Why Node.js Needs Asynchronous Code</h2>
<p>Node.js runs on a single thread. If you tried to handle everything synchronously, your entire server would block while waiting for slow operations like reading files, querying databases, or making HTTP requests.</p>
<p>Think of it like a restaurant. If the waiter takes an order and then stands still waiting for the chef to finish cooking before serving another customer, he wastes time. But if the waiter takes an order, moves to the next customer, and comes back when the food is ready, he serves everyone efficiently.</p>
<p>Node.js works the same way. Asynchronous code lets your server accept multiple requests without blocking.</p>
<h2>Callbacks: The Original Async Solution</h2>
<p>A callback is a function you pass to another function. It gets executed when an operation finishes.</p>
<p>Here's a simple example of reading a file with a callback:</p>
<pre><code class="language-javascript">import fs from 'fs';

fs.readFile('data.txt', 'utf8', (error, data) =&gt; {
  if (error) {
    console.log('Error reading file:', error);
  } else {
    console.log('File contents:', data);
  }
});

console.log('Reading file...');
</code></pre>
<p>When <code>readFile</code> starts, it doesn't wait. Instead, it sets up the callback and moves on to the next line. Once the file loads, the callback executes with either the error or the data.</p>
<p>The output shows this clearly:</p>
<pre><code class="language-plaintext">Reading file...
File contents: [content of data.txt]
</code></pre>
<p>Notice that "Reading file..." prints first, even though it comes after <code>readFile</code> in the code. That's asynchronous execution.</p>
<h2>The Problem: Callback Hell</h2>
<p>Callbacks work fine for a single operation. But what if you need to do multiple things in sequence? You need to read one file, then use its contents to read another file, then process that data.</p>
<pre><code class="language-javascript">import fs from 'fs';

fs.readFile('users.txt', 'utf8', (error1, users) =&gt; {
  if (error1) {
    console.log('Error:', error1);
  } else {
    fs.readFile('posts.txt', 'utf8', (error2, posts) =&gt; {
      if (error2) {
        console.log('Error:', error2);
      } else {
        fs.readFile('comments.txt', 'utf8', (error3, comments) =&gt; {
          if (error3) {
            console.log('Error:', error3);
          } else {
            console.log('All files loaded:', users, posts, comments);
          }
        });
      }
    });
  }
});
</code></pre>
<p>This deeply nested structure is called "callback hell" or the "pyramid of doom." It's hard to read, error-prone, and difficult to maintain. Imagine if you needed five or ten operations. The code would become unreadable.</p>
<h2>Promises: A Better Way</h2>
<p>Promises were introduced to solve callback hell. A promise is an object that represents a value that might be available now, later, or never.</p>
<p>A promise has three states:</p>
<ol>
<li><p><strong>Pending</strong>: The operation hasn't finished yet.</p>
</li>
<li><p><strong>Fulfilled</strong>: The operation completed successfully.</p>
</li>
<li><p><strong>Rejected</strong>: The operation failed.</p>
</li>
</ol>
<p>Here's how you create a promise:</p>
<pre><code class="language-javascript">const promise = new Promise((resolve, reject) =&gt; {
  // Do something async
  if (success) {
    resolve(value); // fulfilled
  } else {
    reject(error); // rejected
  }
});
</code></pre>
<p>The callback receives two functions: <code>resolve</code> and <code>reject</code>. You call <code>resolve</code> when the operation succeeds and <code>reject</code> when it fails.</p>
<h2>Using Promises with <code>.then()</code></h2>
<p>Once you have a promise, you can attach handlers using <code>.then()</code>:</p>
<pre><code class="language-javascript">import fs from 'fs/promises';

fs.readFile('data.txt', 'utf8')
  .then(data =&gt; {
    console.log('File contents:', data);
  })
  .catch(error =&gt; {
    console.log('Error:', error);
  });
</code></pre>
<p>The <code>.then()</code> method runs when the promise is fulfilled, and <code>.catch()</code> runs if it's rejected.</p>
<h2>Chaining Promises</h2>
<p>The real power of promises comes from chaining them. Each <code>.then()</code> returns a new promise, so you can chain multiple operations:</p>
<pre><code class="language-javascript">import fs from 'fs/promises';

fs.readFile('users.txt', 'utf8')
  .then(users =&gt; {
    console.log('Users loaded');
    return fs.readFile('posts.txt', 'utf8');
  })
  .then(posts =&gt; {
    console.log('Posts loaded');
    return fs.readFile('comments.txt', 'utf8');
  })
  .then(comments =&gt; {
    console.log('Comments loaded');
    console.log('All files loaded successfully');
  })
  .catch(error =&gt; {
    console.log('Error:', error);
  });
</code></pre>
<p>This is much cleaner than nested callbacks. Each step is at the same indentation level, making it easy to follow the flow.</p>
<h2>Promise Lifecycle Visualization</h2>
<p>Here's how a promise moves through its states:</p>
<pre><code class="language-plaintext">Promise Created (Pending)
         |
    -----+-----
   |          |
Resolved    Rejected
(Fulfilled)  (Error)
   |          |
.then()     .catch()
</code></pre>
<p>Once a promise is settled (either fulfilled or rejected), it doesn't change. You can attach multiple <code>.then()</code> handlers, and they all receive the same result.</p>
<h2>Error Handling with Promises</h2>
<p>Promises make error handling cleaner. A single <code>.catch()</code> handles errors from any step in the chain:</p>
<pre><code class="language-javascript">import fs from 'fs/promises';

fs.readFile('file1.txt', 'utf8')
  .then(data1 =&gt; fs.readFile('file2.txt', 'utf8'))
  .then(data2 =&gt; fs.readFile('file3.txt', 'utf8'))
  .then(data3 =&gt; console.log('All files loaded'))
  .catch(error =&gt; console.log('Error in any step:', error));
</code></pre>
<p>If any <code>.then()</code> throws an error or rejects, the <code>.catch()</code> will handle it. You don't need to check for errors at each step.</p>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Forgetting to return promises in chains</strong></p>
<pre><code class="language-javascript">// Wrong
fs.readFile('file1.txt', 'utf8')
  .then(data1 =&gt; {
    fs.readFile('file2.txt', 'utf8'); // Missing return
  })
  .then(data2 =&gt; {
    // data2 will be undefined
  });

// Correct
fs.readFile('file1.txt', 'utf8')
  .then(data1 =&gt; {
    return fs.readFile('file2.txt', 'utf8');
  })
  .then(data2 =&gt; {
    // data2 has the file contents
  });
</code></pre>
<p><strong>Mistake 2: Not catching rejected promises</strong></p>
<pre><code class="language-javascript">// Can leave unhandled rejections
fs.readFile('nonexistent.txt', 'utf8')
  .then(data =&gt; console.log(data));

// Better
fs.readFile('nonexistent.txt', 'utf8')
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.log(error));
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>Node.js is single-threaded, so asynchronous code is essential to avoid blocking.</p>
</li>
<li><p>Callbacks were the original async solution but lead to callback hell with nested operations.</p>
</li>
<li><p>Promises provide a cleaner way to handle async operations with <code>.then()</code> and <code>.catch()</code>.</p>
</li>
<li><p>Promise chains let you handle multiple sequential operations readably.</p>
</li>
<li><p>A single <code>.catch()</code> handles errors from any step in the chain.</p>
</li>
</ul>
<p>Understanding callbacks and promises is fundamental to working with Node.js. Once you're comfortable with these, async/await (which we'll cover separately) makes async code look almost like synchronous code while keeping the benefits of non-blocking execution.</p>
]]></content:encoded></item><item><title><![CDATA[Blocking vs Non-Blocking Code in Node.js]]></title><description><![CDATA[One fundamental concept separates great Node.js developers from frustrated ones: understanding blocking vs non-blocking code. Write blocking code, and your entire server freezes. Write non-blocking co]]></description><link>https://blog.ashish.pro/blocking-vs-non-blocking-code-nodejs</link><guid isPermaLink="true">https://blog.ashish.pro/blocking-vs-non-blocking-code-nodejs</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Sun, 19 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/158b09ba-059e-4a2b-9f03-f1d3f01df1a6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One fundamental concept separates great Node.js developers from frustrated ones: understanding blocking vs non-blocking code. Write blocking code, and your entire server freezes. Write non-blocking code, and it handles thousands of concurrent users.</p>
<h2>What Is Blocking Code?</h2>
<p>Blocking code stops execution until an operation completes.</p>
<pre><code class="language-javascript">const fs = require('fs');

console.log('Starting');
const data = fs.readFileSync('file.txt'); // Waits here
console.log('File read');
console.log(data);
</code></pre>
<p>While <code>readFileSync</code> reads the file, nothing else happens. Your code is frozen. If this is a server handling requests, every other request waits.</p>
<p>Execution timeline:</p>
<pre><code class="language-plaintext">0ms: Start
0ms: Call readFileSync
100ms: File read, execution continues
100ms: Log file content
</code></pre>
<p>The server is blocked for 100ms. If you have 100 simultaneous requests, they queue up for 100ms each. Total wait time: 10,000ms (10 seconds).</p>
<h2>What Is Non-Blocking Code?</h2>
<p>Non-blocking code delegates work and continues immediately:</p>
<pre><code class="language-javascript">import fs from 'fs';

console.log('Starting');
fs.readFile('file.txt', (err, data) =&gt; {
  console.log('File read');
  console.log(data);
});
console.log('Continuing');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Starting
Continuing
File read
[file content]
</code></pre>
<p>The file read happens in the background. Your code continues immediately.</p>
<p>Execution timeline:</p>
<pre><code class="language-plaintext">0ms: Start
0ms: Delegate file read
0ms: Log "Continuing"
100ms: File read completes, callback executes
</code></pre>
<p>The server never blocks. It can handle other requests while waiting.</p>
<h2>Real-World Impact</h2>
<p>Let's simulate a server handling three requests, each requiring a 1-second file read:</p>
<p><strong>Blocking Code:</strong></p>
<pre><code class="language-javascript">app.get('/data', (req, res) =&gt; {
  const data = fs.readFileSync('file.txt'); // Blocks for 1 second
  res.send(data);
});
</code></pre>
<p>Timeline:</p>
<pre><code class="language-plaintext">0s: Request 1 arrives, starts reading
1s: Request 1 done, Response 1 sent
1s: Request 2 arrives (was waiting), starts reading
2s: Request 2 done, Response 2 sent
2s: Request 3 arrives (was waiting), starts reading
3s: Request 3 done, Response 3 sent
</code></pre>
<p>Total time for all users: 3 seconds. Avg user wait: 2 seconds.</p>
<p><strong>Non-Blocking Code:</strong></p>
<pre><code class="language-javascript">app.get('/data', (req, res) =&gt; {
  fs.readFile('file.txt', (err, data) =&gt; {
    res.send(data);
  });
});
</code></pre>
<p>Timeline:</p>
<pre><code class="language-plaintext">0s: Request 1 arrives, delegates read, returns immediately
0s: Request 2 arrives, delegates read, returns immediately
0s: Request 3 arrives, delegates read, returns immediately
1s: All reads complete, all responses sent
</code></pre>
<p>Total time for all users: 1 second. Avg user wait: ~1 second.</p>
<p>Non-blocking is 3x faster with just 3 requests. With 100 requests, the difference is massive.</p>
<h2>Blocking Operations in Node.js</h2>
<p>Common operations that have synchronous (blocking) versions:</p>
<pre><code class="language-javascript">import fs from 'fs';

// Blocking
fs.readFileSync('file.txt');
fs.writeFileSync('file.txt', 'data');

// Non-blocking alternatives
fs.readFile('file.txt', (err, data) =&gt; {});
fs.writeFile('file.txt', 'data', (err) =&gt; {});
</code></pre>
<p><strong>Database operations:</strong></p>
<pre><code class="language-javascript">// Blocking (bad)
const result = db.querySync('SELECT * FROM users');

// Non-blocking (good)
db.query('SELECT * FROM users', (err, result) =&gt; {});
</code></pre>
<p><strong>HTTP requests:</strong></p>
<pre><code class="language-javascript">// Blocking (bad - if such a function existed)
const response = http.getSync('https://api.example.com/data');

// Non-blocking (good)
fetch('https://api.example.com/data')
  .then(res =&gt; res.json())
  .then(data =&gt; console.log(data));
</code></pre>
<h2>Why Does Blocking Happen?</h2>
<p>Some operations in Node.js are inherently slow:</p>
<ul>
<li><p>Reading from disk (slower than RAM)</p>
</li>
<li><p>Network requests (very slow)</p>
</li>
<li><p>Database queries (slow)</p>
</li>
<li><p>Complex calculations (time-consuming)</p>
</li>
</ul>
<p>Node.js can't speed these up. But it can handle them without blocking the thread by delegating to background workers.</p>
<h2>The Event Loop Revisited</h2>
<p>Understanding the event loop explains the difference:</p>
<pre><code class="language-javascript">import fs from 'fs';

console.log('1');
fs.readFile('file.txt', () =&gt; {
  console.log('2');
});
console.log('3');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">1
3
2
</code></pre>
<p>Here's what happens:</p>
<pre><code class="language-plaintext">1. JavaScript starts
   - Logs "1"
   - Delegates file read (tells worker thread)
   - Logs "3"
   - No more code

2. Event loop checks: Is anything ready?
   - File read not done yet, nothing to do

3. Event loop checks: Is anything ready?
   - File is ready! Execute callback
   - Logs "2"
</code></pre>
<p>The callback waits in a queue until the main thread is free.</p>
<h2>Async/Await: Cleaner Syntax</h2>
<p>While callbacks are non-blocking, async/await is cleaner:</p>
<pre><code class="language-javascript">import fs from 'fs/promises';

// Using callbacks (non-blocking)
fs.readFile('file.txt', (err, data) =&gt; {
  console.log(data);
});

// Using async/await (still non-blocking!)
const data = await fs.readFile('file.txt');
console.log(data);
</code></pre>
<p>Async/await looks like synchronous code but it's non-blocking. When you <code>await</code> a slow operation, the function pauses, but the thread continues handling other requests.</p>
<h2>Practical Examples</h2>
<p><strong>Bad: Synchronous (blocking)</strong></p>
<pre><code class="language-javascript">import express from 'express';
import fs from 'fs';

const app = express();

app.get('/read-file', (req, res) =&gt; {
  const data = fs.readFileSync('large-file.txt'); // Blocks entire server
  res.send(data);
});

app.listen(3000);
</code></pre>
<p><strong>Good: Asynchronous (non-blocking)</strong></p>
<pre><code class="language-javascript">import express from 'express';
import fs from 'fs/promises';

const app = express();

app.get('/read-file', async (req, res) =&gt; {
  try {
    const data = await fs.readFile('large-file.txt');
    res.send(data);
  } catch (error) {
    res.status(500).send('Error reading file');
  }
});

app.listen(3000);
</code></pre>
<p><strong>Bad: Database operations (blocking)</strong></p>
<pre><code class="language-javascript">app.get('/users', (req, res) =&gt; {
  const users = db.querySync('SELECT * FROM users'); // Blocks server
  res.json(users);
});
</code></pre>
<p><strong>Good: Database operations (non-blocking)</strong></p>
<pre><code class="language-javascript">app.get('/users', async (req, res) =&gt; {
  const users = await db.query('SELECT * FROM users');
  res.json(users);
});
</code></pre>
<h2>Mixing Blocking and Non-Blocking</h2>
<p>If even one route is blocking, it can impact others:</p>
<pre><code class="language-javascript">import express from 'express';
import fs from 'fs';
import fs_async from 'fs/promises';

const app = express();

// This route blocks the entire server
app.get('/slow', (req, res) =&gt; {
  const data = fs.readFileSync('huge-file.txt'); // BLOCKS
  res.send(data);
});

// This route is fast but blocked by /slow
app.get('/fast', (req, res) =&gt; {
  res.send('Fast response');
});

// If someone calls /slow, requests to /fast must wait!
</code></pre>
<p>One blocking operation can bring down your entire server.</p>
<h2>CPU-Intensive Code</h2>
<p>Even pure JavaScript computations can block:</p>
<pre><code class="language-javascript">// Blocks the thread for 5 seconds
function expensiveCalculation() {
  let result = 0;
  for (let i = 0; i &lt; 1_000_000_000; i++) {
    result += Math.sqrt(i);
  }
  return result;
}

app.get('/calculate', (req, res) =&gt; {
  const result = expensiveCalculation(); // Blocks thread
  res.json({ result });
});
</code></pre>
<p>For CPU-intensive work, use worker threads:</p>
<pre><code class="language-javascript">import { Worker } from 'worker_threads';
import path from 'path';

app.get('/calculate', (req, res) =&gt; {
  const worker = new Worker('./worker.js');
  worker.on('message', (result) =&gt; {
    res.json({ result });
  });
});
</code></pre>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Using</strong> <code>readFileSync</code> <strong>in a server</strong></p>
<pre><code class="language-javascript">// Wrong - blocks entire server
app.get('/data', (req, res) =&gt; {
  const data = fs.readFileSync('file.txt');
  res.send(data);
});

// Right - non-blocking
app.get('/data', async (req, res) =&gt; {
  const data = await fs.readFile('file.txt');
  res.send(data);
});
</code></pre>
<p><strong>Mistake 2: Not awaiting async operations</strong></p>
<pre><code class="language-javascript">// Wrong - doesn't wait for query
app.get('/users', async (req, res) =&gt; {
  const users = db.query('SELECT * FROM users');
  res.json(users); // users is undefined
});

// Right - waits for query
app.get('/users', async (req, res) =&gt; {
  const users = await db.query('SELECT * FROM users');
  res.json(users);
});
</code></pre>
<p><strong>Mistake 3: Blocking in middleware</strong></p>
<pre><code class="language-javascript">// Wrong - blocking middleware
app.use((req, res, next) =&gt; {
  const user = db.querySync('SELECT * FROM users WHERE id=1');
  req.user = user;
  next();
});

// Right - non-blocking middleware
app.use(async (req, res, next) =&gt; {
  req.user = await db.query('SELECT * FROM users WHERE id=1');
  next();
});
</code></pre>
<h2>Key Takeaways</h2>
<ul>
<li><p>Blocking code stops execution until operations complete</p>
</li>
<li><p>Non-blocking code delegates work and continues immediately</p>
</li>
<li><p>In a server, one blocking operation can block all other requests</p>
</li>
<li><p>Node.js provides non-blocking alternatives to synchronous functions</p>
</li>
<li><p>Use <code>async/await</code> for cleaner non-blocking code</p>
</li>
<li><p>Avoid <code>.Sync</code> methods (readFileSync, etc.) in servers</p>
</li>
<li><p>Even one blocking route can cripple server performance</p>
</li>
<li><p>CPU-intensive tasks should use worker threads</p>
</li>
</ul>
<p>Embracing non-blocking code is what separates Node.js code that barely works from code that scales massively. Master this concept, and you've mastered Node.js.</p>
]]></content:encoded></item><item><title><![CDATA[How Node.js Handles Multiple Requests with a Single Thread]]></title><description><![CDATA[One of the most confusing things about Node.js is that it runs on a single thread, yet it can handle thousands of concurrent requests. This seems like a contradiction. How does a single thread handle ]]></description><link>https://blog.ashish.pro/how-nodejs-handles-multiple-requests-single-thread</link><guid isPermaLink="true">https://blog.ashish.pro/how-nodejs-handles-multiple-requests-single-thread</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Fri, 17 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/55c3f410-ae11-4220-ad9b-12282420c0d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the most confusing things about Node.js is that it runs on a single thread, yet it can handle thousands of concurrent requests. This seems like a contradiction. How does a single thread handle multiple requestsa without blocking? The answer lies in understanding concurrency versus parallelism and the event loop.</p>
<h2>Single-Threaded: What It Means</h2>
<p>Node.js runs your JavaScript code on a single thread. There's only one thread executing your code at any given moment.</p>
<p>Contrast this with languages like Java or Python, where you might create a new thread for each request. With Node.js, you can't do that. You get one thread, and that's it.</p>
<p>If you tried to handle requests synchronously and sequentially, your server would be painfully slow. The first request would block all others.</p>
<h2>Concurrency vs Parallelism</h2>
<p>This is critical to understand:</p>
<p><strong>Parallelism</strong> means doing multiple things at the exact same time. You need multiple processors or cores.</p>
<p><strong>Concurrency</strong> means managing multiple tasks without necessarily doing them at the same time. You interleave tasks.</p>
<p>Node.js achieves concurrency, not parallelism, through its single thread.</p>
<p>Think of a restaurant again. One waiter (single thread) can handle many customers (requests) concurrently by:</p>
<ol>
<li><p>Taking customer A's order</p>
</li>
<li><p>Passing it to the kitchen (delegating work)</p>
</li>
<li><p>Taking customer B's order</p>
</li>
<li><p>Checking if customer A's food is ready</p>
</li>
<li><p>Serving customer A</p>
</li>
<li><p>Taking customer C's order</p>
</li>
<li><p>Checking if customer B's food is ready</p>
</li>
<li><p>Serving customer B</p>
</li>
</ol>
<p>The waiter isn't cooking (just like Node.js doesn't perform database queries itself). The waiter is coordinating and serving, while the kitchen does the actual work.</p>
<h2>The Event Loop and Worker Threads</h2>
<p>When you perform slow operations like reading files or querying databases, Node.js doesn't have your thread wait. Instead, it delegates the work to background workers.</p>
<p>Here's what happens:</p>
<pre><code class="language-javascript">import fs from 'fs';

console.log('Starting');

fs.readFile('large-file.txt', 'utf8', (err, data) =&gt; {
  console.log('File read complete');
});

console.log('Continuing');
</code></pre>
<p><strong>Execution flow:</strong></p>
<ol>
<li><p>"Starting" is logged</p>
</li>
<li><p><code>readFile</code> is called, work is delegated to a background worker</p>
</li>
<li><p>"Continuing" is logged immediately (no wait!)</p>
</li>
<li><p>Background worker reads the file</p>
</li>
<li><p>Once complete, the callback is queued</p>
</li>
<li><p>Event loop picks up the callback and executes it</p>
</li>
<li><p>"File read complete" is logged</p>
</li>
</ol>
<p>Output:</p>
<pre><code class="language-plaintext">Starting
Continuing
File read complete
</code></pre>
<p>Your thread never blocks. It quickly delegates work and moves on to handle other things.</p>
<h2>How Node.js Handles Multiple Requests</h2>
<p>Here's a more realistic server example:</p>
<pre><code class="language-javascript">import express from 'express';
import fs from 'fs';

const app = express();

app.get('/read-file', (req, res) =&gt; {
  fs.readFile('data.txt', 'utf8', (err, data) =&gt; {
    if (err) {
      res.status(500).send('Error reading file');
    } else {
      res.send(data);
    }
  });
});

app.get('/db-query', (req, res) =&gt; {
  // Simulating a database query that takes 1 second
  setTimeout(() =&gt; {
    res.send('Query complete');
  }, 1000);
});

app.listen(3000, () =&gt; console.log('Server running'));
</code></pre>
<p>Imagine two requests arrive at almost the same time:</p>
<ol>
<li><p><strong>Request 1</strong> comes in → Server starts reading a file → Work delegated to background thread</p>
</li>
<li><p><strong>Request 2</strong> comes in → Server performs a database query → Work delegated to background thread</p>
</li>
<li><p>Node.js thread is free and waiting for events</p>
</li>
<li><p>File read completes → Callback executed → Response sent to client 1</p>
</li>
<li><p>Database query completes → Callback executed → Response sent to client 2</p>
</li>
</ol>
<p>Both operations happened concurrently, but your JavaScript code only ran on one thread. The thread quickly delegates work and moves on.</p>
<h2>Event Loop Visualization</h2>
<p>The event loop continuously checks if there's work to do:</p>
<pre><code class="language-plaintext">Main Thread (JavaScript execution)
         |
    -----+-----
   |          |
Sync Code  Callbacks
   |          |
   |    Event Loop checks:
   |    1. Is there a completed operation?
   |    2. Execute its callback
   |    3. Go back to step 1
</code></pre>
<p>Here's a concrete example:</p>
<pre><code class="language-javascript">console.log('1: Start');

setTimeout(() =&gt; {
  console.log('2: Timer complete');
}, 100);

console.log('3: Still on main thread');
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-plaintext">1: Start
3: Still on main thread
2: Timer complete
</code></pre>
<p>Even though the timer was set first, the synchronous code runs first. The callback waits until the main thread is free.</p>
<h2>Why This Scales Well</h2>
<p>This design is powerful because:</p>
<ol>
<li><p><strong>No thread overhead</strong>: Creating a thread for each request is expensive. Node.js uses a single thread plus a pool of background workers.</p>
</li>
<li><p><strong>Efficient resource usage</strong>: A single thread uses far less memory than hundreds of threads.</p>
</li>
<li><p><strong>Avoids context switching</strong>: The operating system doesn't have to switch between many threads.</p>
</li>
<li><p><strong>Non-blocking by default</strong>: Slow operations don't block other requests.</p>
</li>
</ol>
<p>A server might handle 10,000 concurrent connections with a single thread because most of that time is spent waiting for databases, files, or network responses—none of which block the thread.</p>
<h2>The Blocking Problem</h2>
<p>What happens if you ignore this and write blocking code?</p>
<pre><code class="language-javascript">import express from 'express';
import fs from 'fs';

const app = express();

app.get('/slow-sync', (req, res) =&gt; {
  // This blocks the entire server!
  const data = fs.readFileSync('large-file.txt', 'utf8');
  res.send(data);
});

app.listen(3000);
</code></pre>
<p>If a request to <code>/slow-sync</code> takes 2 seconds, <strong>every other request</strong> waits 2 seconds. The single thread is occupied and can't handle other requests.</p>
<p>With 100 simultaneous requests, they queue up and wait for the thread. This destroys performance.</p>
<h2>Worker Threads for CPU-Intensive Work</h2>
<p>For CPU-intensive operations (like complex calculations), Node.js has worker threads. You can offload heavy computations to prevent blocking:</p>
<pre><code class="language-javascript">import express from 'express';
import { Worker } from 'worker_threads';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.get('/cpu-intensive', (req, res) =&gt; {
  // Delegate to a worker thread
  const worker = new Worker(path.join(__dirname, 'worker.js'));
  
  worker.on('message', (result) =&gt; {
    res.json({ result });
  });
  
  worker.postMessage({ data: 'process this' });
});

app.listen(3000);
</code></pre>
<p>This keeps the main thread responsive while a worker does the heavy computation.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>Node.js runs JavaScript on a single thread, achieving concurrency, not parallelism</p>
</li>
<li><p>Slow operations like I/O are delegated to background workers</p>
</li>
<li><p>While workers are busy, the main thread handles other requests</p>
</li>
<li><p>The event loop coordinates everything</p>
</li>
<li><p>This model scales well and uses resources efficiently</p>
</li>
<li><p>Blocking code (synchronous operations) kills performance</p>
</li>
<li><p>For CPU-intensive tasks, use worker threads</p>
</li>
<li><p>The single-threaded model is a feature, not a limitation</p>
</li>
</ul>
<p>Understanding this is key to writing efficient Node.js applications. Don't block the thread, let it coordinate work, and your server will handle massive concurrent load.</p>
]]></content:encoded></item><item><title><![CDATA[The Node.js Event Loop Explained]]></title><description><![CDATA[The event loop is the heart of Node.js. It's what makes Node.js single-threaded yet capable of handling thousands of concurrent requests. Understanding it is crucial to writing efficient Node.js appli]]></description><link>https://blog.ashish.pro/nodejs-event-loop-explained</link><guid isPermaLink="true">https://blog.ashish.pro/nodejs-event-loop-explained</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Wed, 15 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/81f01eae-cc6a-4f03-a1e6-f7daab5b329e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The event loop is the heart of Node.js. It's what makes Node.js single-threaded yet capable of handling thousands of concurrent requests. Understanding it is crucial to writing efficient Node.js applications.</p>
<h2>Why Node.js Needs an Event Loop</h2>
<p>Node.js runs on a single thread. There's one thread executing your JavaScript code. But a single thread would be useless if it blocked on every slow operation.</p>
<p>The event loop is the solution. It lets you execute code non-blocking. While waiting for operations to complete, the event loop checks what's ready and executes callbacks.</p>
<h2>The Basic Concept</h2>
<p>Imagine a task manager:</p>
<ol>
<li><p>You give the manager tasks</p>
</li>
<li><p>The manager executes tasks one by one</p>
</li>
<li><p>For tasks that take time (like filing documents), the manager delegates to workers</p>
</li>
<li><p>Once workers finish, the manager executes the callback</p>
</li>
<li><p>The manager never sits idle waiting</p>
</li>
</ol>
<p>This is the event loop.</p>
<h2>How the Event Loop Works</h2>
<p>When you run Node.js:</p>
<pre><code class="language-javascript">console.log('Start');

setTimeout(() =&gt; {
  console.log('Timeout');
}, 100);

console.log('End');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Start
End
Timeout
</code></pre>
<p>Here's what happens behind the scenes:</p>
<pre><code class="language-plaintext">1. Main JavaScript code executes
   - Logs "Start"
   - setTimeout schedules callback (doesn't execute now)
   - Logs "End"
   - Main code complete

2. Event loop checks: Is anything ready?
   - 100ms haven't passed, nothing ready
   
3. 100ms passes

4. Event loop checks: Is anything ready?
   - Timeout callback is ready! Execute it
   - Logs "Timeout"

5. No more callbacks, event loop waits
</code></pre>
<p>The event loop is constantly checking: "Is there anything I need to do?"</p>
<h2>Call Stack and Callback Queue</h2>
<p>The event loop manages two things:</p>
<p><strong>Call Stack</strong>: Where your code executes</p>
<p><strong>Callback Queue</strong>: Where callbacks wait to execute</p>
<pre><code class="language-plaintext">Your Code
   |
   v
Call Stack (currently executing)
   |
   v (if done)
Event Loop (checks: anything in queue?)
   |
   v
Callback Queue (waiting callbacks)
</code></pre>
<p>When the call stack is empty, the event loop picks a callback from the queue and executes it.</p>
<h2>Visualizing the Event Loop</h2>
<p>Here's a more detailed flow:</p>
<pre><code class="language-javascript">console.log('1');

setTimeout(() =&gt; {
  console.log('2');
}, 0);

console.log('3');
</code></pre>
<p>Timeline:</p>
<pre><code class="language-plaintext">Time 0ms:
- Call Stack: [console.log('1')]
- Execute: Logs '1'
- Call Stack is empty

Time 0ms:
- Call Stack: [setTimeout callback scheduled]
- Callback added to queue
- Call Stack is empty

Time 0ms:
- Call Stack: [console.log('3')]
- Execute: Logs '3'
- Call Stack is empty

Time 0ms (main code done):
- Event Loop checks queue
- Callback queue has the setTimeout callback
- Move callback to call stack
- Execute: Logs '2'
- Call Stack is empty

Done
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">1
3
2
</code></pre>
<p>Even though <code>setTimeout</code> had 0ms delay, it doesn't execute immediately. It's always after the main code finishes.</p>
<h2>Phases of the Event Loop</h2>
<p>The event loop has several phases. Each phase handles different types of operations:</p>
<pre><code class="language-plaintext">┌───────────────────────────┐
│      Event Loop Phases    │
├───────────────────────────┤
│   1. Timers               │ (setTimeout, setInterval)
│   2. Pending Callbacks    │ (OS operations)
│   3. Idle, Prepare        │ (Internal)
│   4. Poll                 │ (I/O operations)
│   5. Check                │ (setImmediate)
│   6. Close Callbacks      │ (Socket close)
└───────────────────────────┘
</code></pre>
<p>The event loop cycles through these phases repeatedly.</p>
<h2>File I/O and the Event Loop</h2>
<p>File operations demonstrate the event loop perfectly:</p>
<pre><code class="language-javascript">import fs from 'fs';

console.log('Start');

fs.readFile('file.txt', (err, data) =&gt; {
  console.log('File read');
});

console.log('End');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Start
End
File read
</code></pre>
<p>Timeline:</p>
<pre><code class="language-plaintext">1. Main code starts
2. Logs "Start"
3. fs.readFile called - work delegated to file system worker
4. Logs "End"
5. Main code done

6. Event loop checks: Is file read done?
7. No, not yet

8. ...time passes...

9. Event loop checks: Is file read done?
10. Yes! Add callback to queue

11. Event loop executes callback from queue
12. Logs "File read"
</code></pre>
<p>Your code never waits. The file is read in the background while your code continues.</p>
<h2>setTimeout vs setImmediate</h2>
<p>Both defer execution, but they're in different phases:</p>
<pre><code class="language-javascript">setImmediate(() =&gt; console.log('Immediate'));
setTimeout(() =&gt; console.log('Timeout'), 0);
</code></pre>
<p>Output (usually):</p>
<pre><code class="language-plaintext">Timeout
Immediate
</code></pre>
<p><code>setTimeout</code> executes in the Timers phase, <code>setImmediate</code> in the Check phase. The event loop visits Timers first.</p>
<h2>Microtasks vs Macrotasks</h2>
<p>There's a subtle complexity: microtasks and macrotasks.</p>
<p><strong>Microtasks</strong> (execute immediately after each operation):</p>
<ul>
<li><p>Promises (.then, .catch, .finally)</p>
</li>
<li><p>process.nextTick()</p>
</li>
</ul>
<p><strong>Macrotasks</strong> (queued in phases):</p>
<ul>
<li><p>setTimeout</p>
</li>
<li><p>setInterval</p>
</li>
<li><p>setImmediate</p>
</li>
<li><p>I/O operations</p>
</li>
</ul>
<p>Here's the key: After each macrotask, the event loop executes ALL microtasks before moving to the next macrotask.</p>
<pre><code class="language-javascript">console.log('Start');

setTimeout(() =&gt; {
  console.log('Timeout');
}, 0);

Promise.resolve()
  .then(() =&gt; console.log('Promise 1'))
  .then(() =&gt; console.log('Promise 2'));

console.log('End');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Start
End
Promise 1
Promise 2
Timeout
</code></pre>
<p>Timeline:</p>
<pre><code class="language-plaintext">1. Main code: Logs "Start"
2. Main code: setTimeout scheduled (to Macrotask queue)
3. Main code: Promises created and scheduled (to Microtask queue)
4. Main code: Logs "End"
5. Main code done

6. Event loop checks: Any microtasks?
7. Yes! Execute Promise .then
8. Logs "Promise 1"

9. Microtask queue still has one
10. Execute it
11. Logs "Promise 2"

12. No more microtasks
13. Event loop checks: Any macrotasks?
14. Yes! setTimeout callback
15. Execute it
16. Logs "Timeout"
</code></pre>
<p>Promises always execute before setTimeout, even with 0ms delay!</p>
<h2>Practical Example</h2>
<p>Here's a realistic scenario:</p>
<pre><code class="language-javascript">import fs from 'fs/promises';

console.log('1: Start');

setTimeout(() =&gt; {
  console.log('2: setTimeout');
}, 0);

fs.readFile('file.txt', 'utf8')
  .then(data =&gt; {
    console.log('3: File read (Promise)');
  });

Promise.resolve()
  .then(() =&gt; console.log('4: Promise'));

console.log('5: End');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">1: Start
5: End
4: Promise
3: File read (Promise)
2: setTimeout
</code></pre>
<p>Explanation:</p>
<ol>
<li><p>Synchronous code runs first (1, 5)</p>
</li>
<li><p>Microtasks run (4)</p>
</li>
<li><p>File read completes and resolves (3) - still a microtask</p>
</li>
<li><p>Macrotasks run (2)</p>
</li>
</ol>
<h2>Common Mistakes</h2>
<p><strong>Mistake 1: Assuming setTimeout runs immediately</strong></p>
<pre><code class="language-javascript">setTimeout(() =&gt; {
  console.log('runs second');
}, 0);

console.log('runs first');
</code></pre>
<p>Even with 0ms, <code>setTimeout</code> callback doesn't run immediately. It waits until the main code finishes.</p>
<p><strong>Mistake 2: Long-running operations block the event loop</strong></p>
<pre><code class="language-javascript">// Bad - blocks event loop for 5 seconds
function expensive() {
  for (let i = 0; i &lt; 1_000_000_000; i++) {
    Math.sqrt(i);
  }
}

setTimeout(() =&gt; {
  expensive(); // Blocks entire event loop
}, 100);

// While expensive() runs, no other callbacks execute
</code></pre>
<p><strong>Mistake 3: Thinking setImmediate is faster</strong></p>
<pre><code class="language-javascript">setImmediate(() =&gt; console.log('1'));
setTimeout(() =&gt; console.log('2'), 0);
</code></pre>
<p>setImmediate doesn't necessarily run first. It depends on whether the current phase is the Check phase.</p>
<h2>Debugging the Event Loop</h2>
<p>You can visualize event loop behavior:</p>
<pre><code class="language-javascript">const start = Date.now();

setTimeout(() =&gt; {
  console.log(`Timer: ${Date.now() - start}ms`);
}, 100);

Promise.resolve()
  .then(() =&gt; console.log(`Promise 1: ${Date.now() - start}ms`))
  .then(() =&gt; console.log(`Promise 2: ${Date.now() - start}ms`));

console.log(`Main: ${Date.now() - start}ms`);
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Main: 0ms
Promise 1: 0ms
Promise 2: 0ms
Timer: 102ms
</code></pre>
<p>The promises run immediately (microtasks), the timer waits for its delay.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>The event loop continuously checks what work is ready to do</p>
</li>
<li><p>Your JavaScript code runs on the main thread, blocking the event loop</p>
</li>
<li><p>I/O operations run on background workers, not blocking the event loop</p>
</li>
<li><p>Callbacks wait in queues until the event loop picks them up</p>
</li>
<li><p>Microtasks (Promises) always run before macrotasks (timers, I/O)</p>
</li>
<li><p>Even <code>setTimeout(..., 0)</code> doesn't run immediately</p>
</li>
<li><p>Long-running code blocks the entire event loop</p>
</li>
<li><p>Understanding the event loop is key to writing efficient Node.js</p>
</li>
</ul>
<p>The event loop isn't magic. It's a simple idea: check what's ready, execute it, repeat. Master this concept, and you'll write Node.js code that scales and performs well.</p>
]]></content:encoded></item><item><title><![CDATA[Setting Up Your First Node.js Application Step-by-Step]]></title><description><![CDATA[You want to start with Node.js but don't know where to begin. This guide walks you through installing Node.js, verifying it works, and running your first code. By the end, you'll have a working Node.j]]></description><link>https://blog.ashish.pro/setup-first-nodejs-app-step-by-step</link><guid isPermaLink="true">https://blog.ashish.pro/setup-first-nodejs-app-step-by-step</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Mon, 13 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/a5deecc5-6868-4df7-a87e-784519f81cf4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You want to start with Node.js but don't know where to begin. This guide walks you through installing Node.js, verifying it works, and running your first code. By the end, you'll have a working Node.js application.</p>
<h2>Step 1: Install Node.js</h2>
<p>Visit <a href="https://nodejs.org">nodejs.org</a> and download the LTS (Long Term Support) version. This is the stable, recommended version for most users.</p>
<p>The installer includes both Node.js and npm (Node Package Manager), which you'll need for managing code libraries.</p>
<p>Run the installer and follow the default options. Once complete, open a terminal and verify the installation:</p>
<pre><code class="language-bash">node --version
npm --version
</code></pre>
<p>You should see version numbers:</p>
<pre><code>v18.16.0
9.6.7
</code></pre>
<p>If you see version numbers, you're good. If you get "command not found," try restarting your terminal or computer.</p>
<h2>Step 2: Create a Project Folder</h2>
<p>Open your terminal and create a folder for your project:</p>
<pre><code class="language-bash">mkdir my-first-app
cd my-first-app
</code></pre>
<p>Navigate into this folder. Everything you create will live here.</p>
<h2>Step 3: Initialize npm</h2>
<p>Initialize npm to create a <code>package.json</code> file:</p>
<pre><code class="language-bash">npm init -y
</code></pre>
<p>The <code>-y</code> flag skips questions and creates a default <code>package.json</code>. This file tracks your project's metadata and dependencies.</p>
<p>You'll see a new <code>package.json</code> file in your folder.</p>
<h2>Step 4: Create Your First Script</h2>
<p>Create a file called <code>hello.js</code>:</p>
<pre><code class="language-bash">touch hello.js
</code></pre>
<p>Open it in your editor and write:</p>
<pre><code class="language-javascript">console.log('Hello, Node.js!');
</code></pre>
<p>This is the simplest possible Node.js program. It just prints a message.</p>
<h2>Step 5: Run Your Script</h2>
<p>In your terminal, run:</p>
<pre><code class="language-bash">node hello.js
</code></pre>
<p>You should see:</p>
<pre><code>Hello, Node.js!
</code></pre>
<p>Congratulations! You've run your first Node.js script.</p>
<h2>Step 6: Understand the Node REPL</h2>
<p>The REPL (Read-Eval-Print Loop) is an interactive JavaScript environment. It's useful for experimenting.</p>
<p>Type <code>node</code> in your terminal with no filename:</p>
<pre><code class="language-bash">node
</code></pre>
<p>You'll see a <code>&gt;</code> prompt:</p>
<pre><code>&gt;
</code></pre>
<p>Now you can type JavaScript and execute it immediately:</p>
<pre><code class="language-javascript">&gt; console.log('Testing')
Testing
undefined
&gt; 2 + 2
4
&gt; const greeting = 'Hello'
undefined
&gt; greeting
'Hello'
</code></pre>
<p>To exit the REPL, press <code>Ctrl+C</code> twice or type <code>.exit</code> and press Enter.</p>
<p>The REPL is great for testing small pieces of code before adding them to your scripts.</p>
<h2>Step 7: Work with Files</h2>
<p>Node.js comes with the <code>fs</code> module for reading and writing files. Let's create a data file and read it.</p>
<p>Create a file called <code>data.txt</code>:</p>
<pre><code class="language-bash">echo "This is some data" &gt; data.txt
</code></pre>
<p>Now create a script called <code>read-file.js</code>:</p>
<pre><code class="language-javascript">import fs from 'fs';

fs.readFile('data.txt', 'utf8', (err, data) =&gt; {
  if (err) {
    console.log('Error reading file:', err);
  } else {
    console.log('File contents:', data);
  }
});
</code></pre>
<p>Run it:</p>
<pre><code class="language-bash">node read-file.js
</code></pre>
<p>Output:</p>
<pre><code>File contents: This is some data
</code></pre>
<p>You're reading a file asynchronously. Node.js reads the file without blocking, and calls the callback when done.</p>
<h2>Step 8: Create a Simple Web Server</h2>
<p>Now let's create a basic HTTP server:</p>
<p>Create a file called <code>server.js</code>:</p>
<pre><code class="language-javascript">import http from 'http';

const hostname = 'localhost';
const port = 3000;

const server = http.createServer((req, res) =&gt; {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello from Node.js server!');
});

server.listen(port, hostname, () =&gt; {
  console.log(`Server running at http://\({hostname}:\){port}/`);
});
</code></pre>
<p>Run it:</p>
<pre><code class="language-bash">node server.js
</code></pre>
<p>You'll see:</p>
<pre><code>Server running at http://localhost:3000/
</code></pre>
<p>Open your browser and visit <code>http://localhost:3000/</code>. You'll see your message!</p>
<p>To stop the server, press <code>Ctrl+C</code> in the terminal.</p>
<h2>Project Structure Overview</h2>
<p>Your project folder now looks like:</p>
<pre><code>my-first-app/
  package.json
  hello.js
  data.txt
  read-file.js
  server.js
</code></pre>
<p>Each file is a standalone script you can run with <code>node filename.js</code>.</p>
<h2>Understanding package.json</h2>
<p>Your <code>package.json</code> looks something like:</p>
<pre><code class="language-json">{
  "name": "my-first-app",
  "version": "1.0.0",
  "type": "module",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
</code></pre>
<p>Key fields:</p>
<ul>
<li><strong>name</strong>: Your project's name</li>
<li><strong>version</strong>: Version number</li>
<li><strong>type</strong>: Set to "module" for ES imports</li>
<li><strong>main</strong>: Entry point file</li>
<li><strong>scripts</strong>: Commands you can run with <code>npm run</code></li>
<li><strong>dependencies</strong>: Packages your project needs (currently empty)</li>
</ul>
<h2>Installing Packages</h2>
<p>Node.js has an enormous ecosystem of packages. Install them with npm.</p>
<p>For example, install Express (a popular web framework):</p>
<pre><code class="language-bash">npm install express
</code></pre>
<p>This downloads Express and saves it in a <code>node_modules/</code> folder. It also updates <code>package.json</code>:</p>
<pre><code class="language-json">{
  "dependencies": {
    "express": "^4.18.2"
  }
}
</code></pre>
<p>Now you can use Express in your code:</p>
<pre><code class="language-javascript">import express from 'express';

const app = express();
app.get('/', (req, res) =&gt; {
  res.send('Hello with Express!');
});
app.listen(3000);
</code></pre>
<h2>Common npm Commands</h2>
<pre><code class="language-bash"># Install a package
npm install package-name

# Install packages listed in package.json
npm install

# Run a script from package.json
npm run script-name

# List installed packages
npm list

# Update packages
npm update
</code></pre>
<h2>Tips for Success</h2>
<p><strong>Use meaningful file names</strong>: <code>server.js</code>, <code>utils.js</code>, <code>models.js</code> tell you what each file does.</p>
<p><strong>Keep it organized</strong>: As projects grow, organize files into folders like <code>src/</code>, <code>models/</code>, <code>routes/</code>.</p>
<p><strong>Comment your code</strong>: Explain why you're doing things, not just what you're doing.</p>
<p><strong>Use the REPL for learning</strong>: Test small snippets before adding them to files.</p>
<p><strong>Check documentation</strong>: When stuck, Google it or check the official Node.js docs at nodejs.org.</p>
<h2>What's Next?</h2>
<p>You've learned:</p>
<ul>
<li>How to install and run Node.js</li>
<li>How to create and run scripts</li>
<li>How to use the REPL</li>
<li>How to read files</li>
<li>How to create a basic HTTP server</li>
<li>How to manage packages with npm</li>
</ul>
<p>From here, you can explore:</p>
<ul>
<li>Express.js for building web applications</li>
<li>Connecting to databases</li>
<li>Building REST APIs</li>
<li>Understanding async code deeper</li>
</ul>
<h2>Key Takeaways</h2>
<ul>
<li>Install Node.js from nodejs.org</li>
<li>Create scripts in <code>.js</code> files</li>
<li>Run scripts with <code>node filename.js</code></li>
<li>Use the REPL for experimentation</li>
<li><code>package.json</code> manages your project and dependencies</li>
<li>npm installs and manages packages</li>
<li>Node.js has built-in modules like <code>fs</code> and <code>http</code></li>
</ul>
<p>You now have a solid foundation. Practice creating small scripts, experimenting in the REPL, and building simple servers. The more you practice, the more comfortable Node.js becomes.</p>
]]></content:encoded></item><item><title><![CDATA[What is Node.js? JavaScript on the Server Explained]]></title><description><![CDATA[You've heard about Node.js everywhere. Maybe you've wondered: "Why would anyone run JavaScript outside the browser?" This guide explains what Node.js is, why it exists, and why developers love it.
Bef]]></description><link>https://blog.ashish.pro/what-is-nodejs-javascript-server-explained</link><guid isPermaLink="true">https://blog.ashish.pro/what-is-nodejs-javascript-server-explained</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Sun, 12 Apr 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6969f0b76dcf8b8da031011d/880ea4a7-1a9f-4573-849c-8fac936f1093.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You've heard about Node.js everywhere. Maybe you've wondered: "Why would anyone run JavaScript outside the browser?" This guide explains what Node.js is, why it exists, and why developers love it.</p>
<h2>Before Node.js: JavaScript Was Browser-Only</h2>
<p>JavaScript started in 1995 as a language for browsers. Its job was to add interactivity to web pages: validating forms, showing/hiding elements, animating things.</p>
<p>It worked great for that. But it had a limitation: it only ran in browsers. If you wanted to build a server, you used PHP, Python, Java, or other languages.</p>
<p>This meant developers had to learn two languages:</p>
<ul>
<li><p>JavaScript for the frontend</p>
</li>
<li><p>Another language for the backend</p>
</li>
</ul>
<h2>Node.js: JavaScript on the Server</h2>
<p>In 2009, Ryan Dahl created Node.js. It brought JavaScript runtime to servers.</p>
<p>Now developers could write both frontend and backend in JavaScript. One language, multiple environments.</p>
<pre><code class="language-javascript">// Frontend (browser)
document.getElementById('button').addEventListener('click', () =&gt; {
  console.log('Button clicked');
});

// Backend (Node.js)
import express from 'express';
const app = express();
app.get('/api/data', (req, res) =&gt; {
  res.json({ message: 'Data from server' });
});
</code></pre>
<p>Same language, different contexts.</p>
<h2>What Exactly Is Node.js?</h2>
<p>Node.js is a JavaScript runtime. A runtime is an environment where code executes.</p>
<p>A programming language is one thing. A runtime is the environment that understands and runs that language.</p>
<p>Think of it like this:</p>
<ul>
<li><p><strong>Language</strong>: JavaScript (the syntax and rules)</p>
</li>
<li><p><strong>Runtime</strong>: Browser (for frontend), Node.js (for backend)</p>
</li>
</ul>
<p>Node.js uses the V8 engine, the same JavaScript engine that powers Google Chrome. V8 compiles JavaScript to machine code, making it fast.</p>
<h2>Why JavaScript, Though?</h2>
<p>Developers already knew JavaScript. Instead of learning a new language for the server, they could use JavaScript everywhere.</p>
<p>Also, JavaScript is event-driven and asynchronous by design. This fits perfectly with server applications that handle many concurrent requests.</p>
<h2>Node.js Architecture: High Level</h2>
<p>When you run a Node.js application, here's what happens:</p>
<pre><code class="language-plaintext">Your JavaScript Code
        |
        v
Node.js Runtime
  (V8 Engine + APIs)
        |
        v
Operating System
  (File system, networking, etc.)
        |
        v
Hardware
</code></pre>
<p>V8 compiles your JavaScript to machine code. The Node.js runtime provides APIs like <code>fs</code> (file system) and <code>http</code> (networking) that your code can use.</p>
<h2>Node.js vs Browser JavaScript</h2>
<p>Both run JavaScript, but differently:</p>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>Browser</th>
<th>Node.js</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Purpose</strong></td>
<td>User interface</td>
<td>Server, CLI tools, servers</td>
</tr>
<tr>
<td><strong>APIs</strong></td>
<td>DOM, fetch, window</td>
<td>fs, http, path</td>
</tr>
<tr>
<td><strong>File access</strong></td>
<td>Restricted</td>
<td>Full file system access</td>
</tr>
<tr>
<td><strong>Modules</strong></td>
<td>ES modules</td>
<td>ES modules, CommonJS</td>
</tr>
<tr>
<td><strong>Globals</strong></td>
<td>window, document</td>
<td>global, process</td>
</tr>
<tr>
<td><strong>Typical use</strong></td>
<td>Web apps</td>
<td>APIs, services, scripts</td>
</tr>
</tbody></table>
<p>In a browser, you manipulate the DOM. In Node.js, you work with the file system and network.</p>
<h2>Real-World Use Cases</h2>
<p><strong>Building APIs and Web Servers:</strong></p>
<pre><code class="language-javascript">import express from 'express';
const app = express();
app.get('/api/users', (req, res) =&gt; res.json({ users: [] }));
app.listen(3000);
</code></pre>
<p><strong>Command-line Tools:</strong></p>
<pre><code class="language-bash"># Tools like npm, webpack, eslint are built with Node.js
npm install
webpack build
eslint --fix
</code></pre>
<p><strong>Real-time Applications:</strong> Node.js handles concurrent connections well, perfect for chat apps, collaborative tools, live updates.</p>
<p><strong>Scripting and Automation:</strong> Automate tasks, process files, generate reports.</p>
<p><strong>Building single-page applications:</strong> Tools like webpack, Vite, and Next.js use Node.js.</p>
<h2>The Event-Driven Architecture</h2>
<p>Node.js is event-driven. Instead of threads doing work, events trigger handlers.</p>
<pre><code class="language-javascript">import fs from 'fs';

console.log('Starting...');

fs.readFile('data.txt', (err, data) =&gt; {
  console.log('File read complete');
});

console.log('Continuing...');
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Starting...
Continuing...
File read complete
</code></pre>
<p>The file read doesn't block. While waiting, Node.js handles other things. When the file is ready, an event fires, triggering the callback.</p>
<p>This is why Node.js handles many concurrent requests efficiently. Most time is spent waiting for databases, files, or network responses. Node.js doesn't waste processor time on waiting.</p>
<h2>Companies Using Node.js</h2>
<p>Node.js powers major applications:</p>
<ul>
<li><p><strong>Netflix</strong>: Node.js for UI and streaming</p>
</li>
<li><p><strong>Uber</strong>: Real-time ride matching</p>
</li>
<li><p><strong>LinkedIn</strong>: Real-time updates</p>
</li>
<li><p><strong>Walmart</strong>: API backend</p>
</li>
<li><p><strong>Airbnb</strong>: Backend services</p>
</li>
</ul>
<p>These companies chose Node.js because it's fast, scalable, and lets them reuse developers across frontend and backend.</p>
<h2>Node.js Ecosystem: npm</h2>
<p>npm (Node Package Manager) is a package repository. Hundreds of thousands of packages are available, from HTTP frameworks to image processing.</p>
<p>Install packages easily:</p>
<pre><code class="language-bash">npm install express
npm install lodash
npm install axios
</code></pre>
<p>This ecosystem is massive and active. Most problems you encounter, someone's already solved with a package.</p>
<h2>Performance Considerations</h2>
<p>Node.js is fast for I/O-bound tasks (database queries, file reads, HTTP requests). Most web applications are I/O-bound.</p>
<p>It's less ideal for CPU-intensive tasks (image processing, complex calculations). For those, you'd use worker threads or other languages.</p>
<p>But for typical web applications and APIs, Node.js performance is excellent.</p>
<h2>Getting Started</h2>
<p>To start with Node.js:</p>
<ol>
<li><p>Install from nodejs.org</p>
</li>
<li><p>Create a file: <code>hello.js</code></p>
</li>
<li><p>Write code: <code>console.log('Hello')</code></p>
</li>
<li><p>Run: <code>node hello.js</code></p>
</li>
</ol>
<p>That's it. You're running JavaScript on your server.</p>
<h2>Node.js vs Other Backend Languages</h2>
<table>
<thead>
<tr>
<th>Language</th>
<th>Strengths</th>
<th>Typical Use</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Node.js</strong></td>
<td>Async, JavaScript everywhere</td>
<td>Web APIs, real-time apps</td>
</tr>
<tr>
<td><strong>Python</strong></td>
<td>Readable, ML/data science</td>
<td>Data science, scripting</td>
</tr>
<tr>
<td><strong>Java</strong></td>
<td>Mature, OOP, enterprise</td>
<td>Large systems, enterprises</td>
</tr>
<tr>
<td><strong>Go</strong></td>
<td>Simple, concurrent, fast</td>
<td>Microservices, DevOps</td>
</tr>
<tr>
<td><strong>PHP</strong></td>
<td>Easy deployment, shared hosting</td>
<td>Web hosting, WordPress</td>
</tr>
</tbody></table>
<p>Each language has tradeoffs. Node.js excels at I/O-heavy applications where you want full JavaScript from frontend to backend.</p>
<h2>Key Takeaways</h2>
<ul>
<li><p>Node.js is a JavaScript runtime that lets you run JavaScript on servers</p>
</li>
<li><p>It uses the V8 engine, the same engine that powers Chrome</p>
</li>
<li><p>It's event-driven and asynchronous, perfect for handling many concurrent requests</p>
</li>
<li><p>The npm ecosystem is massive with hundreds of thousands of packages</p>
</li>
<li><p>Many major companies use Node.js for their backend infrastructure</p>
</li>
<li><p>It's ideal for I/O-bound applications like web APIs and real-time apps</p>
</li>
<li><p>Learning Node.js lets you build full-stack applications in JavaScript</p>
</li>
</ul>
<p>Node.js democratized backend development. You no longer need to learn multiple languages. JavaScript everywhere has become reality.</p>
<p>If you know JavaScript, you can build servers. That's the power of Node.js.</p>
]]></content:encoded></item><item><title><![CDATA[Error Handling in JavaScript: Try, Catch, Finally Explained]]></title><description><![CDATA[Introduction
Imagine you're using a mobile app and something goes wrong. Instead of the app crashing and closing unexpectedly, it shows a helpful error message and lets you continue using it. That's g]]></description><link>https://blog.ashish.pro/javascript-error-handling-try-catch-finally</link><guid isPermaLink="true">https://blog.ashish.pro/javascript-error-handling-try-catch-finally</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Thu, 26 Mar 2026 05:00:00 GMT</pubDate><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2>
<p>Imagine you're using a mobile app and something goes wrong. Instead of the app crashing and closing unexpectedly, it shows a helpful error message and lets you continue using it. That's good error handling. In JavaScript, errors are inevitable. Whether it's a user entering invalid data, a network request failing, or a bug in your code, knowing how to handle errors gracefully is essential for building reliable applications. In this blog, we'll explore what errors are, how to catch and handle them properly, and why error handling is so important for professional development.</p>
<hr />
<h2><strong>1. What Are Errors in JavaScript?</strong></h2>
<p>Errors are exceptional conditions that occur during program execution. They can be caused by mistakes in code, unexpected user input, or external factors like network failures.</p>
<p><strong>Types of JavaScript Errors:</strong></p>
<pre><code class="language-javascript">// 1. Syntax Error - Invalid code structure (caught at parse time)
// var x = ; // SyntaxError: Unexpected token

// 2. ReferenceError - Using undefined variable
console.log(undefinedVariable); // ReferenceError: undefinedVariable is not defined

// 3. TypeError - Using a value incorrectly
const str = "hello";
str.toUpperCase.toUpperCase(); // TypeError: str.toUpperCase.toUpperCase is not a function

// 4. RangeError - Invalid numeric value
const arr = new Array(-1); // RangeError: Invalid array length

// 5. Error - Generic error
throw new Error("Something went wrong!");
</code></pre>
<p><strong>How Errors Occur in Real Situations:</strong></p>
<pre><code class="language-javascript">// Example 1: Accessing property of undefined
const user = null;
console.log(user.name); // TypeError: Cannot read property 'name' of null

// Example 2: Calling undefined function
const getData = undefined;
getData(); // TypeError: getData is not a function

// Example 3: Invalid operation
const num = "5";
const result = num.map(x =&gt; x); // TypeError: num.map is not a function

// Example 4: Division by zero (not an error, returns Infinity)
console.log(10 / 0); // Infinity (No error!)

// Example 5: JSON parsing invalid JSON
const json = "{invalid json}";
JSON.parse(json); // SyntaxError: Unexpected token
</code></pre>
<p><strong>Error Object Structure:</strong></p>
<pre><code class="language-javascript">try {
  undefinedFunc();
} catch (error) {
  console.log(error.name); // "ReferenceError"
  console.log(error.message); // "undefinedFunc is not defined"
  console.log(error.stack); // Full stack trace for debugging
}
</code></pre>
<hr />
<h2><strong>2. Using Try and Catch Blocks</strong></h2>
<p>The <code>try</code> and <code>catch</code> blocks are the primary way to handle errors in JavaScript. <code>try</code> contains code that might throw an error, and <code>catch</code> handles it if it does.</p>
<p><strong>Basic Structure:</strong></p>
<pre><code class="language-javascript">try {
  // Code that might cause an error
  riskyOperation();
} catch (error) {
  // Code to handle the error
  console.log("An error occurred:", error.message);
}
</code></pre>
<p><strong>Simple Example:</strong></p>
<pre><code class="language-javascript">try {
  const result = JSON.parse("invalid json");
} catch (error) {
  console.log("Failed to parse JSON:", error.message);
  // Program continues instead of crashing
}

console.log("Program continues..."); // This still executes!
</code></pre>
<p><strong>Catching Specific Error Types:</strong></p>
<pre><code class="language-javascript">// Without try-catch - program crashes here
const user = null;
console.log(user.name); // TypeError: Cannot read property 'name' of null
console.log("This never executes"); // Never reached

// ----

// With try-catch - handle gracefully
try {
  const user = null;
  console.log(user.name);
} catch (error) {
  console.log("Caught an error:", error.message);
}
console.log("Program continues"); // This executes!
</code></pre>
<p><strong>Checking Error Type:</strong></p>
<pre><code class="language-javascript">try {
  const data = JSON.parse("{invalid}");
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid JSON format");
  } else if (error instanceof TypeError) {
    console.log("Type mismatch");
  } else {
    console.log("Unknown error:", error.message);
  }
}
</code></pre>
<p><strong>Real-World Example: API Call</strong></p>
<pre><code class="language-javascript">async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    
    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`);
    }
    
    const data = await response.json();
    return data;
  } catch (error) {
    console.log("Failed to fetch user:", error.message);
    return null; // Return default value
  }
}

// Usage
const user = await fetchUserData(1);
if (user) {
  console.log(user);
} else {
  console.log("Could not load user data");
}
</code></pre>
<p><strong>Try-Catch with Different Operations:</strong></p>
<pre><code class="language-javascript">try {
  // Operation 1: Accessing property
  const user = { name: "Raj" };
  console.log(user.name); // "Raj" - OK

  // Operation 2: Array access
  const arr = [1, 2, 3];
  console.log(arr[0]); // 1 - OK

  // Operation 3: Function call
  const result = parseInt("123");
  console.log(result); // 123 - OK

  // Operation 4: File operations (would fail if file doesn't exist)
  // const content = fs.readFileSync("/path/to/file.txt");
  
} catch (error) {
  console.log("Error occurred:", error.message);
}
</code></pre>
<p><strong>Multiple Catches (Not Standard, But Useful Pattern):</strong></p>
<pre><code class="language-javascript">// JavaScript doesn't support multiple catch blocks natively
// But you can use if-else inside catch

try {
  // Some risky operation
  throw new TypeError("Invalid type");
} catch (error) {
  if (error instanceof TypeError) {
    console.log("Type Error:", error.message);
  } else if (error instanceof ReferenceError) {
    console.log("Reference Error:", error.message);
  } else {
    console.log("Other Error:", error.message);
  }
}
</code></pre>
<p><strong>Rethrowing Errors:</strong></p>
<pre><code class="language-javascript">// Sometimes you want to handle an error partially and then rethrow it

try {
  riskyOperation();
} catch (error) {
  console.log("Logging error for debugging:", error.message);
  
  // Log to external service
  logErrorToServer(error);
  
  // Rethrow for caller to handle
  throw error;
}
</code></pre>
<hr />
<h2><strong>3. The Finally Block</strong></h2>
<p>The <code>finally</code> block executes regardless of whether an error occurs. It's perfect for cleanup operations.</p>
<p><strong>Basic Structure:</strong></p>
<pre><code class="language-javascript">try {
  // Code that might throw an error
  riskyOperation();
} catch (error) {
  // Handle the error
  console.log("Error:", error.message);
} finally {
  // This ALWAYS runs
  console.log("Cleanup code");
}
</code></pre>
<p><strong>Execution Order:</strong></p>
<pre><code class="language-javascript">console.log("1. Start");

try {
  console.log("2. In try block");
  throw new Error("Something went wrong");
  console.log("3. This won't execute");
} catch (error) {
  console.log("4. In catch block:", error.message);
} finally {
  console.log("5. In finally block");
}

console.log("6. After try-catch-finally");

// Output:
// 1. Start
// 2. In try block
// 4. In catch block: Something went wrong
// 5. In finally block
// 6. After try-catch-finally
</code></pre>
<p><strong>Finally Always Executes (Even with Return):</strong></p>
<pre><code class="language-javascript">function testFinally() {
  try {
    return "from try";
  } finally {
    console.log("finally executes!"); // This executes even with return!
  }
}

const result = testFinally();
console.log(result); // "from try"

// Output:
// finally executes!
// from try
</code></pre>
<p><strong>Practical Use Cases for Finally:</strong></p>
<p><strong>Use Case 1: Closing Resources</strong></p>
<pre><code class="language-javascript">function readFile(filename) {
  let file = null;
  
  try {
    file = openFile(filename); // Open file
    const content = file.read(); // Read content
    return content;
  } catch (error) {
    console.log("Error reading file:", error.message);
    return null;
  } finally {
    if (file) {
      file.close(); // Always close the file
    }
  }
}
</code></pre>
<p><strong>Use Case 2: Cleaning Up State</strong></p>
<pre><code class="language-javascript">let isLoading = false;

async function fetchData(url) {
  isLoading = true;
  
  try {
    const response = await fetch(url);
    return await response.json();
  } catch (error) {
    console.log("Fetch failed:", error.message);
    return null;
  } finally {
    isLoading = false; // Always update loading state
  }
}
</code></pre>
<p><strong>Use Case 3: Hiding Loading Indicator</strong></p>
<pre><code class="language-javascript">function showLoadingSpinner() {
  document.getElementById("spinner").style.display = "block";
}

function hideLoadingSpinner() {
  document.getElementById("spinner").style.display = "none";
}

async function loadUserData(userId) {
  showLoadingSpinner();
  
  try {
    const response = await fetch(`/api/users/${userId}`);
    return await response.json();
  } catch (error) {
    console.log("Failed to load user:", error.message);
    return null;
  } finally {
    hideLoadingSpinner(); // Always hide, even on error
  }
}
</code></pre>
<p><strong>Use Case 4: Database Connection</strong></p>
<pre><code class="language-javascript">async function queryDatabase(query) {
  let connection = null;
  
  try {
    connection = await db.connect();
    const results = await connection.query(query);
    return results;
  } catch (error) {
    console.log("Query failed:", error.message);
    return [];
  } finally {
    if (connection) {
      connection.close(); // Always close connection
    }
  }
}
</code></pre>
<p><strong>Finally with No Catch:</strong></p>
<pre><code class="language-javascript">// You can have try-finally without catch
try {
  const data = JSON.parse(jsonString);
  processData(data);
} finally {
  console.log("Cleanup regardless of errors");
  // If error occurs, it will propagate after finally
}
</code></pre>
<hr />
<h2><strong>4. Throwing Custom Errors</strong></h2>
<p>Sometimes you need to throw your own errors to signal that something went wrong in your application logic.</p>
<p><strong>Throwing Simple Errors:</strong></p>
<pre><code class="language-javascript">// Throw a string (not recommended)
throw "Something went wrong";

// Throw an Error object (recommended)
throw new Error("Something went wrong");
</code></pre>
<p><strong>Creating Custom Error Classes:</strong></p>
<pre><code class="language-javascript">// Custom error class
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

// Using custom error
function validateEmail(email) {
  if (!email.includes("@")) {
    throw new ValidationError("Invalid email format");
  }
  return true;
}

try {
  validateEmail("invalid-email");
} catch (error) {
  if (error instanceof ValidationError) {
    console.log("Validation failed:", error.message);
  }
}
</code></pre>
<p><strong>Practical Example: User Validation</strong></p>
<pre><code class="language-javascript">class ValidationError extends Error {
  constructor(field, message) {
    super(`\({field}: \){message}`);
    this.name = "ValidationError";
    this.field = field;
  }
}

function validateUser(user) {
  if (!user.name || user.name.trim() === "") {
    throw new ValidationError("name", "Name is required");
  }
  
  if (!user.email || !user.email.includes("@")) {
    throw new ValidationError("email", "Valid email is required");
  }
  
  if (!user.age || user.age &lt; 18) {
    throw new ValidationError("age", "Must be 18 or older");
  }
  
  return true;
}

try {
  validateUser({ name: "Ashish", email: "invalid", age: 21 });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`\({error.field}: \){error.message}`);
  }
}
</code></pre>
<p><strong>Throwing Errors Based on Conditions:</strong></p>
<pre><code class="language-javascript">function divide(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("Both arguments must be numbers");
  }
  
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  
  return a / b;
}

try {
  console.log(divide(10, 2)); // 5
  console.log(divide(10, 0)); // Error!
} catch (error) {
  console.log("Calculation error:", error.message);
}
</code></pre>
<p><strong>Multiple Custom Errors:</strong></p>
<pre><code class="language-javascript">class AuthenticationError extends Error {
  constructor(message) {
    super(message);
    this.name = "AuthenticationError";
  }
}

class AuthorizationError extends Error {
  constructor(message) {
    super(message);
    this.name = "AuthorizationError";
  }
}

function login(username, password) {
  if (!username || !password) {
    throw new AuthenticationError("Username and password required");
  }
  
  // Simulate user lookup
  if (username !== "admin") {
    throw new AuthenticationError("Invalid credentials");
  }
  
  return { username, token: "abc123" };
}

function accessAdminPanel(user) {
  if (!user.isAdmin) {
    throw new AuthorizationError("Admin access required");
  }
}

try {
  const user = login("admin", "password");
  accessAdminPanel(user);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.log("Auth Error:", error.message);
  } else if (error instanceof AuthorizationError) {
    console.log("Access Denied:", error.message);
  } else {
    console.log("Unknown error:", error.message);
  }
}
</code></pre>
<p><strong>Throwing with Stack Trace Preservation:</strong></p>
<pre><code class="language-javascript">function processData(data) {
  try {
    const parsed = JSON.parse(data);
    if (!parsed.id) {
      throw new Error("ID field is required");
    }
    return parsed;
  } catch (error) {
    // Add context while preserving stack trace
    console.error("Failed to process data:", error);
    
    // Rethrow with additional context
    throw error; // Stack trace is preserved
  }
}
</code></pre>
<hr />
<h2><strong>5. Why Error Handling Matters</strong></h2>
<p>Proper error handling is critical for professional, reliable applications. Let's understand why.</p>
<p><strong>Reason 1: Prevents Application Crashes</strong></p>
<pre><code class="language-javascript">// Without error handling - app crashes
const userInput = "not a number";
const number = parseInt(userInput);
const result = number * 2; // If something goes wrong, app stops here
console.log("Result:", result); // Never executes on error

// ----

// With error handling - app continues
try {
  const userInput = "not a number";
  const number = parseInt(userInput);
  if (isNaN(number)) {
    throw new Error("Invalid number");
  }
  const result = number * 2;
  console.log("Result:", result);
} catch (error) {
  console.log("Error:", error.message);
  console.log("Using default value"); // Still continues
}
</code></pre>
<p><strong>Reason 2: Better User Experience</strong></p>
<pre><code class="language-javascript">// Without error handling - user sees blank page
async function loadUserProfile(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const user = await response.json(); // If fails, crashes
  displayUserProfile(user);
}

// ----

// With error handling - user sees message
async function loadUserProfile(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    
    if (!response.ok) {
      throw new Error("Failed to load profile");
    }
    
    const user = await response.json();
    displayUserProfile(user);
  } catch (error) {
    showErrorMessage("Could not load profile. Please try again later.");
  }
}
</code></pre>
<p><strong>Reason 3: Easier Debugging</strong></p>
<pre><code class="language-javascript">// Without error handling - hard to debug
function complexOperation() {
  const result1 = operation1(); // Which failed?
  const result2 = operation2(result1); // This?
  const result3 = operation3(result2); // Or this?
  return result3;
}

// ----

// With error handling - clear where it failed
function complexOperation() {
  try {
    const result1 = operation1();
    console.log("Operation 1 succeeded");
    
    const result2 = operation2(result1);
    console.log("Operation 2 succeeded");
    
    const result3 = operation3(result2);
    console.log("Operation 3 succeeded");
    
    return result3;
  } catch (error) {
    console.error("Operation failed:", error.message);
    console.error("Stack trace:", error.stack);
  }
}
</code></pre>
<p><strong>Reason 4: Resource Cleanup</strong></p>
<pre><code class="language-javascript">// Without finally - resources might leak
async function downloadFile(url) {
  const file = await openFile();
  const content = await fetch(url); // What if this fails?
  await file.write(content);
  file.close(); // Never called if fetch fails!
}

// ----

// With finally - always cleanup
async function downloadFile(url) {
  let file = null;
  
  try {
    file = await openFile();
    const content = await fetch(url);
    await file.write(content);
  } catch (error) {
    console.log("Download failed:", error.message);
  } finally {
    if (file) {
      file.close(); // Always closes
    }
  }
}
</code></pre>
<p><strong>Reason 5: Production Monitoring</strong></p>
<pre><code class="language-javascript">// Log errors for monitoring in production
async function criticalOperation() {
  try {
    // Important business logic
    return await processSensitiveData();
  } catch (error) {
    // Log for monitoring
    console.error("Critical operation failed:", error);
    
    // Send to error tracking service
    sendErrorToSentry({
      message: error.message,
      stack: error.stack,
      timestamp: new Date()
    });
    
    // Notify admin
    notifyAdminOfError(error);
    
    // Return safe default
    return { success: false };
  }
}
</code></pre>
<p><strong>Reason 6: Validation and Data Integrity</strong></p>
<pre><code class="language-javascript">// Prevent invalid data from being processed
function saveUserData(userData) {
  try {
    validateUser(userData);
    
    // Only save if validation passes
    database.save(userData);
    
    return { success: true };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

function validateUser(user) {
  if (!user.email || !user.email.includes("@")) {
    throw new Error("Invalid email");
  }
  
  if (!user.age || user.age &lt; 0 || user.age &gt; 150) {
    throw new Error("Invalid age");
  }
}
</code></pre>
<hr />
<h2><strong>6. Error Handling Patterns</strong></h2>
<p><strong>Pattern 1: Try-Catch-Finally</strong></p>
<pre><code class="language-javascript">function fetchData(url) {
  try {
    // Attempt operation
    const response = fetch(url);
    return response;
  } catch (error) {
    // Handle error
    console.log("Fetch failed:", error.message);
    return null;
  } finally {
    // Cleanup
    console.log("Fetch attempt completed");
  }
}
</code></pre>
<p><strong>Pattern 2: Error Wrapping</strong></p>
<pre><code class="language-javascript">// Wrap low-level errors in meaningful ones
function getUserFromDatabase(userId) {
  try {
    return db.query(`SELECT * FROM users WHERE id = ${userId}`);
  } catch (error) {
    throw new Error(`Failed to load user \({userId}: \){error.message}`);
  }
}
</code></pre>
<p><strong>Pattern 3: Graceful Degradation</strong></p>
<pre><code class="language-javascript">// Continue with default values on error
function getUser(userId) {
  try {
    return fetch(`/api/users/${userId}`).then(r =&gt; r.json());
  } catch (error) {
    console.log("Using cached user data");
    return getCachedUser(userId); // Fallback
  }
}
</code></pre>
<p><strong>Pattern 4: Silent Failure (Use Carefully)</strong></p>
<pre><code class="language-javascript">// Sometimes you want to fail silently
function tryParse(json) {
  try {
    return JSON.parse(json);
  } catch (error) {
    // Don't throw, just return null
    return null;
  }
}
</code></pre>
<p><strong>Pattern 5: Error Logging</strong></p>
<pre><code class="language-javascript">// Log errors with context
function logError(error, context) {
  const errorInfo = {
    message: error.message,
    stack: error.stack,
    context: context,
    timestamp: new Date(),
    userAgent: navigator.userAgent
  };
  
  // Send to server
  fetch("/api/errors", {
    method: "POST",
    body: JSON.stringify(errorInfo)
  });
}

try {
  riskyOperation();
} catch (error) {
  logError(error, { operation: "fetchUserData", userId: 123 });
}
</code></pre>
<hr />
<h2><strong>7. Common Error Handling Mistakes</strong></h2>
<p><strong>Mistake 1: Silent Failures</strong></p>
<pre><code class="language-javascript">// Bad: Silently failing
try {
  processData();
} catch (error) {
  // Empty - error is swallowed
}

// Good: Handle or log
try {
  processData();
} catch (error) {
  console.error("Processing failed:", error);
  throw error; // Rethrow if critical
}
</code></pre>
<p><strong>Mistake 2: Catching Everything Without Discrimination</strong></p>
<pre><code class="language-javascript">// Bad: Catches all errors
try {
  const data = JSON.parse(json);
  const result = riskyOperation(data);
  return result;
} catch (error) {
  // Can't tell what failed
  return null;
}

// Good: Be specific
try {
  const data = JSON.parse(json);
  return riskyOperation(data);
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid JSON");
  } else if (error instanceof TypeError) {
    console.log("Type error in operation");
  } else {
    throw error; // Unknown error, rethrow
  }
}
</code></pre>
<p><strong>Mistake 3: Not Cleaning Up Resources</strong></p>
<pre><code class="language-javascript">// Bad: Resource leak
async function download(url) {
  const file = openFile();
  const data = await fetch(url); // If this fails...
  file.write(data);
  file.close(); // Never called!
}

// Good: Always cleanup
async function download(url) {
  const file = openFile();
  try {
    const data = await fetch(url);
    file.write(data);
  } finally {
    file.close(); // Always called
  }
}
</code></pre>
<p><strong>Mistake 4: Losing Original Error Information</strong></p>
<pre><code class="language-javascript">// Bad: Losing stack trace
try {
  riskyOperation();
} catch (error) {
  throw new Error("Failed"); // Stack trace lost
}

// Good: Preserve original error
try {
  riskyOperation();
} catch (error) {
  throw new Error(`Failed: ${error.message}`);
  // Or just rethrow
  throw error;
}
</code></pre>
<p><strong>Mistake 5: Catching Operational Errors as Bugs</strong></p>
<pre><code class="language-javascript">// Bad: Treating user input error as code bug
try {
  const age = parseInt(userInput);
  if (age &lt; 0) throw new Error("Invalid age");
  processAge(age);
} catch (error) {
  // This treats validation error same as code error
  reportBug(error);
}

// Good: Handle validation separately
function validateAge(age) {
  if (age &lt; 0) return false;
  return true;
}

try {
  const age = parseInt(userInput);
  if (!validateAge(age)) {
    showError("Please enter a valid age");
    return;
  }
  processAge(age);
} catch (error) {
  // Only actual code errors reach here
  reportBug(error);
}
</code></pre>
<hr />
<h2><strong>8. Error Handling Flow Diagram</strong></h2>
<pre><code class="language-plaintext">                     Code Execution
                           |
                           v
                    ┌──────────────┐
                    │  try block   │
                    │              │
                    │  Normal code │
                    └──────────────┘
                           |
                ┌──────────┴──────────┐
                |                     |
        No Error Thrown        Error Thrown
                |                     |
                |                     v
                |         ┌──────────────────┐
                |         │ catch block      │
                |         │                  │
                |         │ Error handling   │
                |         └──────────────────┘
                |                     |
                ├─────────────────────┤
                |                     |
                v                     v
         ┌──────────────┐      ┌──────────────┐
         │ finally block│      │ finally block│
         │  (runs)      │      │  (runs)      │
         └──────────────┘      └──────────────┘
                |                     |
                v                     v
         Continue execution  Continue or Rethrow
</code></pre>
<hr />
<h2><strong>9. Real-World Error Handling Example</strong></h2>
<pre><code class="language-javascript">// Complete example combining all concepts

class APIError extends Error {
  constructor(status, message) {
    super(message);
    this.name = "APIError";
    this.status = status;
  }
}

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

async function registerUser(userData) {
  let response = null;
  
  try {
    // Validate input
    validateUserData(userData);
    console.log("✓ Validation passed");
    
    // Make API call
    response = await fetch("/api/register", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(userData)
    });
    
    console.log("✓ API call completed");
    
    // Check response
    if (!response.ok) {
      throw new APIError(response.status, "Registration failed");
    }
    
    const result = await response.json();
    console.log("✓ User registered successfully");
    
    return result;
    
  } catch (error) {
    // Handle different error types
    if (error instanceof ValidationError) {
      console.error(`Validation Error - \({error.field}: \){error.message}`);
      return { success: false, error: error.message, field: error.field };
    } else if (error instanceof APIError) {
      console.error(`API Error \({error.status}: \){error.message}`);
      return { success: false, error: error.message };
    } else {
      console.error("Unexpected error:", error.message);
      return { success: false, error: "An unexpected error occurred" };
    }
    
  } finally {
    console.log("✓ Registration attempt completed");
  }
}

function validateUserData(data) {
  if (!data.email || !data.email.includes("@")) {
    throw new ValidationError("email", "Valid email required");
  }
  
  if (!data.password || data.password.length &lt; 8) {
    throw new ValidationError("password", "Password must be at least 8 characters");
  }
  
  if (!data.name || data.name.trim() === "") {
    throw new ValidationError("name", "Name is required");
  }
}

// Usage
await registerUser({
  name: "Ashish",
  email: "ashish@example.com",
  password: "securePass123"
});
</code></pre>
<hr />
<h2><strong>10. Best Practices for Error Handling</strong></h2>
<p><strong>Best Practice 1: Be Specific</strong></p>
<pre><code class="language-javascript">// Bad: Too generic
try {
  doSomething();
} catch (e) {
  console.log("Error");
}

// Good: Specific and informative
try {
  doSomething();
} catch (error) {
  console.error(`Failed to do something: ${error.message}`);
  console.error(`Stack: ${error.stack}`);
}
</code></pre>
<p><strong>Best Practice 2: Use Custom Errors</strong></p>
<pre><code class="language-javascript">// Bad: Generic errors
throw new Error("Invalid");

// Good: Specific error classes
throw new ValidationError("email", "Invalid email format");
</code></pre>
<p><strong>Best Practice 3: Always Cleanup</strong></p>
<pre><code class="language-javascript">// Always use finally for cleanup
try {
  operation();
} finally {
  cleanup();
}
</code></pre>
<p><strong>Best Practice 4: Log Errors in Production</strong></p>
<pre><code class="language-javascript">try {
  operation();
} catch (error) {
  // Log for debugging
  console.error(error);
  
  // Send to monitoring service
  logToMonitoringService(error);
}
</code></pre>
<p><strong>Best Practice 5: Provide Meaningful Messages</strong></p>
<pre><code class="language-javascript">// Bad
throw new Error("Failed");

// Good
throw new Error(
  `Failed to save user \({userId} to database: \){error.message}`
);
</code></pre>
<hr />
<h2><strong>Conclusion</strong></h2>
<p>Error handling is essential for professional JavaScript development. Here's what we've learned:</p>
<p><strong>Key Concepts:</strong></p>
<ol>
<li><p><strong>Errors</strong> are exceptional conditions during execution</p>
</li>
<li><p><strong>Try-Catch</strong> blocks catch and handle errors</p>
</li>
<li><p><strong>Finally</strong> always runs, perfect for cleanup</p>
</li>
<li><p><strong>Custom Errors</strong> make error handling clear</p>
</li>
<li><p><strong>Error Handling</strong> prevents crashes and improves UX</p>
</li>
</ol>
<p><strong>Why It Matters:</strong></p>
<ul>
<li><p>Prevents application crashes</p>
</li>
<li><p>Improves user experience</p>
</li>
<li><p>Makes debugging easier</p>
</li>
<li><p>Protects resources</p>
</li>
<li><p>Enables monitoring</p>
</li>
</ul>
<p><strong>Best Practices:</strong></p>
<ul>
<li><p>Be specific about what can fail</p>
</li>
<li><p>Use custom error classes</p>
</li>
<li><p>Always cleanup with finally</p>
</li>
<li><p>Log errors for debugging</p>
</li>
<li><p>Provide meaningful error messages</p>
</li>
</ul>
<p><strong>Remember:</strong></p>
<ul>
<li><p>Strings don't throw errors (wrap in Error object)</p>
</li>
<li><p>Finally always executes</p>
</li>
<li><p>Errors can be caught and handled gracefully</p>
</li>
<li><p>Good error handling is a sign of professional code</p>
</li>
</ul>
<p>Master error handling, and your applications will be more robust, reliable, and user-friendly. Every error is an opportunity to provide better feedback and create better experiences.</p>
]]></content:encoded></item><item><title><![CDATA[JavaScript String Methods & Polyfills: Interview Questions You Must Know]]></title><description><![CDATA[Introduction
String manipulation is one of the most fundamental skills in JavaScript programming. Every day, developers work with strings: extracting data, transforming text, searching for patterns, a]]></description><link>https://blog.ashish.pro/javascript-string-methods-polyfills</link><guid isPermaLink="true">https://blog.ashish.pro/javascript-string-methods-polyfills</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Wed, 25 Mar 2026 05:00:00 GMT</pubDate><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2>
<p>String manipulation is one of the most fundamental skills in JavaScript programming. Every day, developers work with strings: extracting data, transforming text, searching for patterns, and validating input. JavaScript provides many built-in string methods that make our lives easier, but understanding how these methods work under the hood is crucial for interviews, debugging, and writing efficient code. In this blog, we'll explore what string methods are, learn how to implement common ones as polyfills, solve real interview questions, and deepen your understanding of string processing in JavaScript.</p>
<hr />
<h2><strong>1. What Are String Methods?</strong></h2>
<p>String methods are functions that operate on strings and perform specific tasks. JavaScript provides many built-in methods, but understanding their logic helps you write better code.</p>
<p><strong>Built-in String Methods Overview:</strong></p>
<pre><code class="language-javascript">const str = "Hello World";

// Length
console.log(str.length); // 11

// Case conversion
console.log(str.toUpperCase()); // "HELLO WORLD"
console.log(str.toLowerCase()); // "hello world"

// Accessing characters
console.log(str.charAt(0)); // "H"
console.log(str[0]); // "H"
console.log(str.charCodeAt(0)); // 72 (ASCII code)

// Searching
console.log(str.indexOf("o")); // 4
console.log(str.includes("World")); // true
console.log(str.startsWith("Hello")); // true
console.log(str.endsWith("World")); // true

// Extracting
console.log(str.substring(0, 5)); // "Hello"
console.log(str.slice(0, 5)); // "Hello"
console.log(str.substr(0, 5)); // "Hello" (deprecated)

// Splitting and joining
console.log(str.split(" ")); // ["Hello", "World"]
console.log("a-b-c".split("-")); // ["a", "b", "c"]

// Replacing
console.log(str.replace("World", "JavaScript")); // "Hello JavaScript"
console.log(str.replaceAll("l", "L")); // "HeLLo WorLd"

// Trimming
console.log("  Hello  ".trim()); // "Hello"
console.log("  Hello  ".trimStart()); // "Hello  "
console.log("  Hello  ".trimEnd()); // "  Hello"

// Repeating and padding
console.log("ab".repeat(3)); // "ababab"
console.log("5".padStart(3, "0")); // "005"
console.log("5".padEnd(3, "0")); // "500"
</code></pre>
<p><strong>String Immutability:</strong></p>
<p>Important: Strings in JavaScript are immutable. Methods don't change the original string, they return a new string.</p>
<pre><code class="language-javascript">const original = "Hello";

const upper = original.toUpperCase();
console.log(original); // "Hello" - Unchanged!
console.log(upper); // "HELLO" - New string

const replaced = original.replace("H", "J");
console.log(original); // "Hello" - Unchanged!
console.log(replaced); // "Jello" - New string
</code></pre>
<hr />
<h2><strong>2. Why Developers Write Polyfills</strong></h2>
<p>A polyfill is code that provides functionality for browsers or environments that don't have it natively. String polyfills are important for several reasons.</p>
<p><strong>Reason 1: Browser Compatibility</strong></p>
<pre><code class="language-javascript">// Some older methods might not exist in older browsers
const str = "Hello World";

// Modern method (ES6+)
if (typeof str.includes !== "function") {
  // Polyfill: provide the functionality
  String.prototype.includes = function(searchStr) {
    return this.indexOf(searchStr) !== -1;
  };
}

console.log(str.includes("World")); // Works even in older browsers
</code></pre>
<p><strong>Reason 2: Understanding Implementation</strong></p>
<pre><code class="language-javascript">// By writing a polyfill, you understand how it works
// This is crucial for interviews!

// Example: Understanding indexOf
function myIndexOf(str, searchStr) {
  for (let i = 0; i &lt; str.length; i++) {
    let found = true;
    for (let j = 0; j &lt; searchStr.length; j++) {
      if (str[i + j] !== searchStr[j]) {
        found = false;
        break;
      }
    }
    if (found) {
      return i;
    }
  }
  return -1;
}

console.log(myIndexOf("Hello World", "World")); // 6
</code></pre>
<p><strong>Reason 3: Interview Preparation</strong></p>
<p>Interviewers often ask you to implement string methods. They want to see:</p>
<ul>
<li><p>Your understanding of string algorithms</p>
</li>
<li><p>Your problem-solving approach</p>
</li>
<li><p>Your code quality and edge case handling</p>
</li>
</ul>
<p><strong>Reason 4: Using in Specific Environments</strong></p>
<pre><code class="language-javascript">// Some methods might not be available in certain environments
// Example: Node.js might not have certain browser-specific methods

// Write a polyfill to ensure consistent behavior
if (!String.prototype.repeat) {
  String.prototype.repeat = function(count) {
    if (count &lt; 0 || count === Infinity) {
      throw new RangeError("Invalid count value");
    }
    let str = "";
    for (let i = 0; i &lt; count; i++) {
      str += this;
    }
    return str;
  };
}
</code></pre>
<p><strong>Reason 5: Custom Extensions</strong></p>
<pre><code class="language-javascript">// Sometimes you extend strings with custom methods
String.prototype.capitalize = function() {
  return this.charAt(0).toUpperCase() + this.slice(1);
};

console.log("hello".capitalize()); // "Hello"

// Or reverse
String.prototype.reverse = function() {
  return this.split("").reverse().join("");
};

console.log("hello".reverse()); // "olleh"
</code></pre>
<hr />
<h2><strong>3. Implementing Simple String Utilities</strong></h2>
<p>Let's implement common string methods to understand how they work.</p>
<p><strong>Implementation 1: charAt()</strong></p>
<pre><code class="language-javascript">// Get character at specific index
String.prototype.myCharAt = function(index) {
  if (index &lt; 0 || index &gt;= this.length) {
    return ""; // Return empty string if index out of bounds
  }
  return this[index];
};

console.log("Hello".myCharAt(0)); // "H"
console.log("Hello".myCharAt(1)); // "e"
console.log("Hello".myCharAt(10)); // ""
</code></pre>
<p><strong>Implementation 2: indexOf()</strong></p>
<pre><code class="language-javascript">// Find the index of a substring
String.prototype.myIndexOf = function(searchStr, fromIndex = 0) {
  if (!searchStr) {
    return 0; // Empty string found at position 0
  }
  
  for (let i = fromIndex; i &lt;= this.length - searchStr.length; i++) {
    let found = true;
    
    // Check if substring matches at position i
    for (let j = 0; j &lt; searchStr.length; j++) {
      if (this[i + j] !== searchStr[j]) {
        found = false;
        break;
      }
    }
    
    if (found) {
      return i;
    }
  }
  
  return -1; // Not found
};

console.log("Hello World".myIndexOf("World")); // 6
console.log("Hello World".myIndexOf("o")); // 4
console.log("Hello World".myIndexOf("xyz")); // -1
console.log("Hello World".myIndexOf("o", 5)); // 7 (finds second 'o')
</code></pre>
<p><strong>Implementation 3: includes()</strong></p>
<pre><code class="language-javascript">// Check if string contains substring
String.prototype.myIncludes = function(searchStr, position = 0) {
  return this.indexOf(searchStr, position) !== -1;
};

console.log("Hello World".myIncludes("World")); // true
console.log("Hello World".myIncludes("xyz")); // false
</code></pre>
<p><strong>Implementation 4: startsWith()</strong></p>
<pre><code class="language-javascript">// Check if string starts with substring
String.prototype.myStartsWith = function(searchStr, position = 0) {
  const substring = this.slice(position);
  return substring.indexOf(searchStr) === 0;
};

console.log("Hello World".myStartsWith("Hello")); // true
console.log("Hello World".myStartsWith("World")); // false
console.log("Hello World".myStartsWith("World", 6)); // true
</code></pre>
<p><strong>Implementation 5: endsWith()</strong></p>
<pre><code class="language-javascript">// Check if string ends with substring
String.prototype.myEndsWith = function(searchStr, length = this.length) {
  const substring = this.slice(0, length);
  return substring.slice(-searchStr.length) === searchStr;
};

console.log("Hello World".myEndsWith("World")); // true
console.log("Hello World".myEndsWith("Hello")); // false
console.log("Hello World".myEndsWith("World", 11)); // true
</code></pre>
<p><strong>Implementation 6: slice()</strong></p>
<pre><code class="language-javascript">// Extract a portion of string
String.prototype.mySlice = function(start = 0, end = this.length) {
  let actualStart = start &lt; 0 ? Math.max(this.length + start, 0) : Math.min(start, this.length);
  let actualEnd = end &lt; 0 ? Math.max(this.length + end, 0) : Math.min(end, this.length);
  
  let result = "";
  for (let i = actualStart; i &lt; actualEnd; i++) {
    result += this[i];
  }
  return result;
};

console.log("Hello World".mySlice(0, 5)); // "Hello"
console.log("Hello World".mySlice(6)); // "World"
console.log("Hello World".mySlice(-5)); // "World"
console.log("Hello World".mySlice(-5, -1)); // "Worl"
</code></pre>
<p><strong>Implementation 7: split()</strong></p>
<pre><code class="language-javascript">// Split string by delimiter
String.prototype.mySplit = function(delimiter = undefined, limit = undefined) {
  const result = [];
  
  if (delimiter === undefined) {
    return [this.toString()];
  }
  
  if (delimiter === "") {
    // Split into individual characters
    for (let i = 0; i &lt; this.length; i++) {
      if (limit &amp;&amp; result.length &gt;= limit) break;
      result.push(this[i]);
    }
    return result;
  }
  
  let currentStr = "";
  for (let i = 0; i &lt; this.length; ) {
    let found = false;
    
    // Check if delimiter starts at position i
    if (i + delimiter.length &lt;= this.length) {
      let match = true;
      for (let j = 0; j &lt; delimiter.length; j++) {
        if (this[i + j] !== delimiter[j]) {
          match = false;
          break;
        }
      }
      
      if (match) {
        result.push(currentStr);
        if (limit &amp;&amp; result.length &gt;= limit) break;
        i += delimiter.length;
        currentStr = "";
        found = true;
      }
    }
    
    if (!found) {
      currentStr += this[i];
      i++;
    }
  }
  
  if (!found || currentStr !== "") {
    if (!limit || result.length &lt; limit) {
      result.push(currentStr);
    }
  }
  
  return result;
};

console.log("a-b-c".mySplit("-")); // ["a", "b", "c"]
console.log("hello".mySplit("")); // ["h", "e", "l", "l", "o"]
console.log("a,b,c,d".mySplit(",", 2)); // ["a", "b"]
</code></pre>
<p><strong>Implementation 8: toUpperCase() and toLowerCase()</strong></p>
<pre><code class="language-javascript">// Convert to uppercase
String.prototype.myToUpperCase = function() {
  let result = "";
  for (let i = 0; i &lt; this.length; i++) {
    const code = this.charCodeAt(i);
    // If lowercase letter (a-z: 97-122)
    if (code &gt;= 97 &amp;&amp; code &lt;= 122) {
      result += String.fromCharCode(code - 32); // Convert to uppercase
    } else {
      result += this[i];
    }
  }
  return result;
};

// Convert to lowercase
String.prototype.myToLowerCase = function() {
  let result = "";
  for (let i = 0; i &lt; this.length; i++) {
    const code = this.charCodeAt(i);
    // If uppercase letter (A-Z: 65-90)
    if (code &gt;= 65 &amp;&amp; code &lt;= 90) {
      result += String.fromCharCode(code + 32); // Convert to lowercase
    } else {
      result[i];
    }
  }
  return result;
};

console.log("Hello".myToUpperCase()); // "HELLO"
console.log("Hello".myToLowerCase()); // "hello"
</code></pre>
<p><strong>Implementation 9: replace()</strong></p>
<pre><code class="language-javascript">// Replace first occurrence
String.prototype.myReplace = function(searchStr, replaceStr) {
  const index = this.indexOf(searchStr);
  
  if (index === -1) {
    return this.toString();
  }
  
  return this.slice(0, index) + replaceStr + this.slice(index + searchStr.length);
};

console.log("Hello World".myReplace("World", "JavaScript")); // "Hello JavaScript"
console.log("Hello Hello".myReplace("Hello", "Hi")); // "Hi Hello" (only first)
</code></pre>
<p><strong>Implementation 10: trim()</strong></p>
<pre><code class="language-javascript">// Remove whitespace from both ends
String.prototype.myTrim = function() {
  let start = 0;
  let end = this.length - 1;
  
  // Find first non-whitespace character
  while (start &lt;= end &amp;&amp; /\s/.test(this[start])) {
    start++;
  }
  
  // Find last non-whitespace character
  while (end &gt;= start &amp;&amp; /\s/.test(this[end])) {
    end--;
  }
  
  return this.slice(start, end + 1);
};

console.log("  Hello World  ".myTrim()); // "Hello World"
console.log("\n\tHello\n".myTrim()); // "Hello"
</code></pre>
<hr />
<h2><strong>4. Common Interview String Problems</strong></h2>
<p>Let's solve classic interview questions about strings.</p>
<p><strong>Problem 1: Reverse a String</strong></p>
<pre><code class="language-javascript">// Problem: Reverse "Hello" → "olleH"

// Approach 1: Using split, reverse, join
function reverseString1(str) {
  return str.split("").reverse().join("");
}

// Approach 2: Using a loop
function reverseString2(str) {
  let result = "";
  for (let i = str.length - 1; i &gt;= 0; i--) {
    result += str[i];
  }
  return result;
}

// Approach 3: Using reduce
function reverseString3(str) {
  return str.split("").reduce((rev, char) =&gt; char + rev, "");
}

// Approach 4: Using recursion
function reverseString4(str) {
  if (str.length &lt;= 1) return str;
  return reverseString4(str.slice(1)) + str[0];
}

const test = "Hello";
console.log(reverseString1(test)); // "olleH"
console.log(reverseString2(test)); // "olleH"
console.log(reverseString3(test)); // "olleH"
console.log(reverseString4(test)); // "olleH"
</code></pre>
<p><strong>Problem 2: Check if String is Palindrome</strong></p>
<pre><code class="language-javascript">// Problem: Is "racecar" a palindrome?

function isPalindrome(str) {
  // Remove spaces and convert to lowercase
  const clean = str.replace(/\s/g, "").toLowerCase();
  
  // Compare with reverse
  const reversed = clean.split("").reverse().join("");
  
  return clean === reversed;
}

// Or: Two-pointer approach
function isPalindrome2(str) {
  const clean = str.replace(/\s/g, "").toLowerCase();
  
  let left = 0;
  let right = clean.length - 1;
  
  while (left &lt; right) {
    if (clean[left] !== clean[right]) {
      return false;
    }
    left++;
    right--;
  }
  
  return true;
}

console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("hello")); // false
console.log(isPalindrome("A man a plan a canal Panama")); // true
</code></pre>
<p><strong>Problem 3: Count Character Occurrences</strong></p>
<pre><code class="language-javascript">// Problem: Count how many times each character appears in "hello"

function countCharacters(str) {
  const count = {};
  
  for (const char of str) {
    count[char] = (count[char] || 0) + 1;
  }
  
  return count;
}

// Or: Using Map
function countCharacters2(str) {
  const count = new Map();
  
  for (const char of str) {
    count.set(char, (count.get(char) || 0) + 1);
  }
  
  return count;
}

console.log(countCharacters("hello"));
// { h: 1, e: 1, l: 2, o: 1 }

console.log(countCharacters2("javascript"));
// Map(8) { 'j' =&gt; 1, 'a' =&gt; 2, 'v' =&gt; 1, 's' =&gt; 1, 'c' =&gt; 1, 'r' =&gt; 1, 'i' =&gt; 1, 't' =&gt; 1 }
</code></pre>
<p><strong>Problem 4: Remove Duplicates from String</strong></p>
<pre><code class="language-javascript">// Problem: "hello" → "helo"

// Approach 1: Using Set
function removeDuplicates1(str) {
  return [...new Set(str)].join("");
}

// Approach 2: Using object/map
function removeDuplicates2(str) {
  const seen = {};
  let result = "";
  
  for (const char of str) {
    if (!seen[char]) {
      result += char;
      seen[char] = true;
    }
  }
  
  return result;
}

// Approach 3: Using filter
function removeDuplicates3(str) {
  return str.split("").filter((char, index, arr) =&gt;
    arr.indexOf(char) === index
  ).join("");
}

console.log(removeDuplicates1("hello")); // "helo"
console.log(removeDuplicates2("programming")); // "progamin"
console.log(removeDuplicates3("aabbcc")); // "abc"
</code></pre>
<p><strong>Problem 5: Longest Substring Without Repeating Characters</strong></p>
<pre><code class="language-javascript">// Problem: "abcabcbb" → "abc" (length 3)

function longestSubstring(str) {
  let maxLength = 0;
  let maxStart = 0;
  let currentStart = 0;
  const seen = {};
  
  for (let i = 0; i &lt; str.length; i++) {
    const char = str[i];
    
    if (seen[char] !== undefined &amp;&amp; seen[char] &gt;= currentStart) {
      // Character seen before in current window
      currentStart = seen[char] + 1;
    }
    
    seen[char] = i;
    
    if (i - currentStart + 1 &gt; maxLength) {
      maxLength = i - currentStart + 1;
      maxStart = currentStart;
    }
  }
  
  return str.slice(maxStart, maxStart + maxLength);
}

console.log(longestSubstring("abcabcbb")); // "abc"
console.log(longestSubstring("bbbbb")); // "b"
console.log(longestSubstring("pwwkew")); // "wke"
console.log(longestSubstring("au")); // "au"
</code></pre>
<p><strong>Problem 6: Check if Two Strings are Anagrams</strong></p>
<pre><code class="language-javascript">// Problem: Are "listen" and "silent" anagrams?

function isAnagram(str1, str2) {
  // Remove spaces and convert to lowercase
  const clean1 = str1.replace(/\s/g, "").toLowerCase();
  const clean2 = str2.replace(/\s/g, "").toLowerCase();
  
  // If lengths differ, not anagrams
  if (clean1.length !== clean2.length) {
    return false;
  }
  
  // Sort and compare
  const sorted1 = clean1.split("").sort().join("");
  const sorted2 = clean2.split("").sort().join("");
  
  return sorted1 === sorted2;
}

// Or: Using character count
function isAnagram2(str1, str2) {
  const clean1 = str1.replace(/\s/g, "").toLowerCase();
  const clean2 = str2.replace(/\s/g, "").toLowerCase();
  
  if (clean1.length !== clean2.length) return false;
  
  const count = {};
  
  for (const char of clean1) {
    count[char] = (count[char] || 0) + 1;
  }
  
  for (const char of clean2) {
    if (!count[char]) {
      return false;
    }
    count[char]--;
  }
  
  return true;
}

console.log(isAnagram("listen", "silent")); // true
console.log(isAnagram("hello", "world")); // false
console.log(isAnagram("Astronomer", "Moon starer")); // true
</code></pre>
<p><strong>Problem 7: Capitalize First Letter of Each Word</strong></p>
<pre><code class="language-javascript">// Problem: "hello world" → "Hello World"

function capitalizeWords1(str) {
  return str
    .split(" ")
    .map(word =&gt; word.charAt(0).toUpperCase() + word.slice(1))
    .join(" ");
}

// Or: Using regex
function capitalizeWords2(str) {
  return str.replace(/\b\w/g, char =&gt; char.toUpperCase());
}

console.log(capitalizeWords1("hello world javascript")); // "Hello World Javascript"
console.log(capitalizeWords2("the quick brown fox")); // "The Quick Brown Fox"
</code></pre>
<p><strong>Problem 8: String Compression</strong></p>
<pre><code class="language-javascript">// Problem: "aaabbc" → "a3b2c1"

function compressString(str) {
  if (!str || str.length &lt;= 1) return str;
  
  let compressed = "";
  let count = 1;
  
  for (let i = 0; i &lt; str.length; i++) {
    // If next character is different or last character
    if (i + 1 &gt;= str.length || str[i] !== str[i + 1]) {
      compressed += str[i] + count;
      count = 1;
    } else {
      count++;
    }
  }
  
  // Return compressed only if it's shorter
  return compressed.length &lt; str.length ? compressed : str;
}

console.log(compressString("abcccccaab")); // "a1b1c5a2b1"
console.log(compressString("abcdef")); // "abcdef" (not shorter)
console.log(compressString("aabbcc")); // "aabbcc" (not shorter)
</code></pre>
<p><strong>Problem 9: First Non-Repeating Character</strong></p>
<pre><code class="language-javascript">// Problem: "leetcode" → "l"

function firstNonRepeating(str) {
  const count = {};
  
  // Count occurrences
  for (const char of str) {
    count[char] = (count[char] || 0) + 1;
  }
  
  // Find first non-repeating
  for (const char of str) {
    if (count[char] === 1) {
      return char;
    }
  }
  
  return null; // All characters repeat
}

console.log(firstNonRepeating("leetcode")); // "l"
console.log(firstNonRepeating("loveleetcode")); // "v"
console.log(firstNonRepeating("aabb")); // null
</code></pre>
<p><strong>Problem 10: Valid Parentheses/Brackets</strong></p>
<pre><code class="language-javascript">// Problem: Are "{[()]}" parentheses valid?

function isValidParentheses(str) {
  const stack = [];
  const pairs = {
    ")": "(",
    "}": "{",
    "]": "["
  };
  
  for (const char of str) {
    if (char === "(" || char === "{" || char === "[") {
      stack.push(char);
    } else if (char === ")" || char === "}" || char === "]") {
      if (stack.length === 0 || stack.pop() !== pairs[char]) {
        return false;
      }
    }
  }
  
  return stack.length === 0;
}

console.log(isValidParentheses("{[()]}")); // true
console.log(isValidParentheses("{[(])}")); // false
console.log(isValidParentheses("()")); // true
console.log(isValidParentheses("({[}])")); // false
</code></pre>
<hr />
<h2><strong>5. Importance of Understanding Built-in Behavior</strong></h2>
<p>Understanding how built-in methods work is crucial for several reasons.</p>
<p><strong>Reason 1: Edge Cases</strong></p>
<pre><code class="language-javascript">// Understanding edge cases prevents bugs

// indexOf with empty string
console.log("hello".indexOf("")); // 0
console.log("hello".indexOf("", 3)); // 3

// slice with negative indices
console.log("hello".slice(-3)); // "llo"
console.log("hello".slice(-3, -1)); // "ll"

// split with empty delimiter
console.log("hello".split("")); // ["h", "e", "l", "l", "o"]

// includes is case-sensitive
console.log("Hello".includes("hello")); // false
</code></pre>
<p><strong>Reason 2: Performance</strong></p>
<pre><code class="language-javascript">// Knowing which methods are efficient

// Bad: Creating many intermediate strings
let result = "";
for (let i = 0; i &lt; 1000; i++) {
  result += "x"; // Creates new string each time - O(n²)
}

// Better: Use join
const arr = [];
for (let i = 0; i &lt; 1000; i++) {
  arr.push("x");
}
const result = arr.join(""); // O(n)

// Or: Use repeat
const result = "x".repeat(1000); // Most efficient
</code></pre>
<p><strong>Reason 3: Regex Integration</strong></p>
<pre><code class="language-javascript">// Many methods work with regex

// replace with regex
const str = "apple apple apple";
console.log(str.replace("apple", "orange")); // "orange apple apple"
console.log(str.replace(/apple/g, "orange")); // "orange orange orange"

// split with regex
console.log("a1b2c3".split(/\d/)); // ["a", "b", "c", ""]

// match with regex
console.log("hello123world456".match(/\d+/g)); // ["123", "456"]
</code></pre>
<p><strong>Reason 4: Unicode Handling</strong></p>
<pre><code class="language-javascript">// Some methods handle Unicode differently

const emoji = "😀😀😀";

console.log(emoji.length); // 6 (each emoji is 2 characters)
console.log([...emoji].length); // 3 (correct count)

// charAt vs square bracket vs [...str]
console.log(emoji.charAt(0)); // "?" (incorrect for emoji)
console.log(emoji[0]); // "?" (incorrect for emoji)
console.log([...emoji][0]); // "😀" (correct)
</code></pre>
<p><strong>Reason 5: Immutability Awareness</strong></p>
<pre><code class="language-javascript">// Strings are immutable - methods return new strings

let str = "hello";
str.toUpperCase(); // Doesn't modify str
console.log(str); // "hello" - unchanged

// Must assign to variable
str = str.toUpperCase();
console.log(str); // "HELLO" - now changed

// This is different from arrays
const arr = [1, 2, 3];
arr.reverse(); // Modifies arr in place
console.log(arr); // [3, 2, 1] - changed
</code></pre>
<hr />
<h2><strong>6. Real-World String Processing Examples</strong></h2>
<p><strong>Example 1: Email Validation</strong></p>
<pre><code class="language-javascript">function validateEmail(email) {
  // Basic email validation
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

console.log(validateEmail("user@example.com")); // true
console.log(validateEmail("invalid.email")); // false
console.log(validateEmail("user@domain.co.uk")); // true
</code></pre>
<p><strong>Example 2: URL Parsing</strong></p>
<pre><code class="language-javascript">function parseUrl(url) {
  const parts = url.split("://");
  const protocol = parts[0];
  
  const remaining = parts[1];
  const pathIndex = remaining.indexOf("/");
  const domain = remaining.slice(0, pathIndex);
  const path = remaining.slice(pathIndex);
  
  return { protocol, domain, path };
}

const result = parseUrl("https://example.com/path/to/page");
console.log(result);
// { protocol: 'https', domain: 'example.com', path: '/path/to/page' }
</code></pre>
<p><strong>Example 3: String Templating</strong></p>
<pre><code class="language-javascript">function formatString(template, data) {
  return template.replace(/\{(\w+)\}/g, (match, key) =&gt; {
    return data[key] || match;
  });
}

const template = "Hello {name}, you are {age} years old";
const data = { name: "Ashish", age: 21 };

console.log(formatString(template, data));
// "Hello Ashish, you are 21 years old"
</code></pre>
<p><strong>Example 4: CSV Parsing</strong></p>
<pre><code class="language-javascript">function parseCSV(csvString) {
  const rows = csvString.split("\n");
  const headers = rows[0].split(",").map(h =&gt; h.trim());
  
  const data = rows.slice(1).map(row =&gt; {
    const values = row.split(",").map(v =&gt; v.trim());
    const obj = {};
    headers.forEach((header, index) =&gt; {
      obj[header] = values[index];
    });
    return obj;
  });
  
  return data;
}

const csv = `Name,Age,City
Raj,25,Jaipur
Priya,23,Delhi
Anil,26,Mumbai`;

console.log(parseCSV(csv));
// [
//   { Name: "Raj", Age: "25", City: "Jaipur" },
//   { Name: "Priya", Age: "23", City: "Delhi" },
//   { Name: "Anil", Age: "26", City: "Mumbai" }
// ]
</code></pre>
<hr />
<h2><strong>7. Interview Tips for String Problems</strong></h2>
<p><strong>Tip 1: Clarify the Problem</strong></p>
<pre><code class="language-javascript">// Before coding, ask questions:
// - Should uppercase and lowercase be treated the same?
// - Should spaces be ignored?
// - What about special characters?
// - What should be returned if not found?

function search(str, pattern, options = {}) {
  const { caseSensitive = true, ignoreSpaces = false } = options;
  
  let searchStr = str;
  let searchPattern = pattern;
  
  if (!caseSensitive) {
    searchStr = searchStr.toLowerCase();
    searchPattern = searchPattern.toLowerCase();
  }
  
  if (ignoreSpaces) {
    searchStr = searchStr.replace(/\s/g, "");
    searchPattern = searchPattern.replace(/\s/g, "");
  }
  
  return searchStr.includes(searchPattern);
}
</code></pre>
<p><strong>Tip 2: Test with Edge Cases</strong></p>
<pre><code class="language-javascript">function reverseString(str) {
  return str.split("").reverse().join("");
}

// Test cases
console.log(reverseString("")); // ""
console.log(reverseString("a")); // "a"
console.log(reverseString("ab")); // "ba"
console.log(reverseString("hello world")); // "dlrow olleh"
console.log(reverseString("123")); // "321"
</code></pre>
<p><strong>Tip 3: Discuss Time and Space Complexity</strong></p>
<pre><code class="language-javascript">// O(n) time, O(n) space - creates new array and string
function method1(str) {
  return str.split("").reverse().join("");
}

// O(n) time, O(n) space - builds new string character by character
function method2(str) {
  let result = "";
  for (let i = str.length - 1; i &gt;= 0; i--) {
    result += str[i];
  }
  return result;
}

// Both are O(n) time and space, but method1 is cleaner
</code></pre>
<p><strong>Tip 4: Show Your Thinking</strong></p>
<pre><code class="language-javascript">// Good: Explain your approach
function isPalindrome(str) {
  // Step 1: Clean the string (remove spaces, convert to lowercase)
  const clean = str.replace(/\s/g, "").toLowerCase();
  
  // Step 2: Compare with reverse
  const reversed = clean.split("").reverse().join("");
  
  // Step 3: Return result
  return clean === reversed;
}

// Walk through with example:
// Input: "A man a plan a canal Panama"
// Step 1: "amanaplanacanalpanama"
// Step 2: "amanaplanacanalpanama"
// Step 3: true
</code></pre>
<p><strong>Tip 5: Optimize If Needed</strong></p>
<pre><code class="language-javascript">// Initial solution
function countVowels(str) {
  let count = 0;
  for (const char of str) {
    if ("aeiouAEIOU".includes(char)) {
      count++;
    }
  }
  return count;
}

// Optimized with Set for faster lookup
function countVowels(str) {
  const vowels = new Set("aeiouAEIOU".split(""));
  let count = 0;
  for (const char of str) {
    if (vowels.has(char)) {
      count++;
    }
  }
  return count;
}

// Or use regex
function countVowels(str) {
  return (str.match(/[aeiou]/gi) || []).length;
}
</code></pre>
<hr />
<h2><strong>8. Common Mistakes to Avoid</strong></h2>
<p><strong>Mistake 1: Forgetting Strings Are Immutable</strong></p>
<pre><code class="language-javascript">// Wrong: Expecting string to change
let str = "hello";
str.toUpperCase(); // Doesn't modify str
console.log(str); // "hello" - still lowercase!

// Right: Assign the result
str = str.toUpperCase();
console.log(str); // "HELLO"
</code></pre>
<p><strong>Mistake 2: Off-by-One Errors with Indices</strong></p>
<pre><code class="language-javascript">const str = "hello";

// Wrong: Misunderstanding slice behavior
console.log(str.slice(0, 2)); // "he" - NOT "hel"!

// Wrong: Using substring when you meant slice
console.log(str.substring(1, 1)); // "" - both params inclusive in some ways

// Right: Remember slice is exclusive on the right
console.log(str.slice(0, 3)); // "hel"
console.log(str.slice(1, 4)); // "ell"
</code></pre>
<p><strong>Mistake 3: Case Sensitivity</strong></p>
<pre><code class="language-javascript">// Wrong: Assuming case-insensitive
console.log("Hello".includes("hello")); // false!

// Right: Convert to same case
console.log("Hello".toLowerCase().includes("hello")); // true
</code></pre>
<p><strong>Mistake 4: Not Handling Empty Strings</strong></p>
<pre><code class="language-javascript">// Wrong: Not checking for empty
function getFirstChar(str) {
  return str[0]; // Returns undefined for empty string
}

// Right: Handle edge case
function getFirstChar(str) {
  return str.length &gt; 0 ? str[0] : "";
}
</code></pre>
<p><strong>Mistake 5: Regex Without Global Flag</strong></p>
<pre><code class="language-javascript">// Wrong: Replaces only first match
const result = "hello hello".replace(/hello/, "hi");
console.log(result); // "hi hello"

// Right: Use global flag for all matches
const result = "hello hello".replace(/hello/g, "hi");
console.log(result); // "hi hi"
</code></pre>
<hr />
<h2><strong>Conclusion</strong></h2>
<p>String manipulation is a fundamental JavaScript skill. Here's what we've learned:</p>
<p><strong>Key Takeaways:</strong></p>
<ol>
<li><p><strong>Understand built-in methods</strong> - Know how charAt(), indexOf(), slice(), split() work</p>
</li>
<li><p><strong>Write polyfills</strong> - Implement methods to understand their logic</p>
</li>
<li><p><strong>Solve interview problems</strong> - Practice reversing, palindromes, anagrams, etc.</p>
</li>
<li><p><strong>Edge cases matter</strong> - Handle empty strings, special characters, Unicode</p>
</li>
<li><p><strong>Performance counts</strong> - Know which methods are efficient</p>
</li>
<li><p><strong>Immutability</strong> - Remember strings don't change, methods return new strings</p>
</li>
</ol>
<p><strong>Interview Preparation:</strong></p>
<ul>
<li><p>Practice common string problems daily</p>
</li>
<li><p>Explain your approach before coding</p>
</li>
<li><p>Test with edge cases</p>
</li>
<li><p>Discuss time and space complexity</p>
</li>
<li><p>Show your thinking process</p>
</li>
</ul>
<p><strong>Real-World Applications:</strong></p>
<ul>
<li><p>Email and URL validation</p>
</li>
<li><p>Text processing and formatting</p>
</li>
<li><p>Data parsing (CSV, JSON)</p>
</li>
<li><p>Search and replace operations</p>
</li>
<li><p>String compression and encryption</p>
</li>
</ul>
<p>Master string methods and polyfill implementation, and you'll be well-prepared for technical interviews and real-world JavaScript development. String manipulation is everywhere - from form validation to data processing - so investing time to understand these concepts deeply will pay dividends throughout your career.</p>
]]></content:encoded></item><item><title><![CDATA[How to Flatten an Array in JavaScript (Multiple Methods Explained)]]></title><description><![CDATA[Introduction
Have you ever worked with data that's organized in multiple layers, like a nested folder structure or a hierarchy of categories? In JavaScript, this is represented as nested arrays - arra]]></description><link>https://blog.ashish.pro/how-to-flatten-an-array-in-javascript-multiple-methods-explained</link><guid isPermaLink="true">https://blog.ashish.pro/how-to-flatten-an-array-in-javascript-multiple-methods-explained</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Tue, 24 Mar 2026 05:00:00 GMT</pubDate><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2>
<p>Have you ever worked with data that's organized in multiple layers, like a nested folder structure or a hierarchy of categories? In JavaScript, this is represented as nested arrays - arrays within arrays. While nested arrays are useful for representing hierarchical data, sometimes you need to flatten them into a single-level array. Array flattening is a common task in real-world applications, from processing API responses to organizing data from databases. In this blog, we'll explore what nested arrays are, why we need to flatten them, and learn multiple techniques to flatten arrays efficiently.</p>
<hr />
<h2><strong>1. What Are Nested Arrays?</strong></h2>
<p>Nested arrays are arrays that contain other arrays as elements. Think of them like Russian dolls, where one doll contains another, which contains another.</p>
<p><strong>Simple Example:</strong></p>
<pre><code class="language-javascript">// Not nested - single level
const simple = [1, 2, 3, 4, 5];
console.log(simple); // [1, 2, 3, 4, 5]

// Nested - contains arrays inside
const nested = [1, [2, 3], 4, [5, 6]];
console.log(nested); // [1, [2, 3], 4, [5, 6]]

// Deeply nested - arrays within arrays within arrays
const deeplyNested = [1, [2, [3, [4, [5]]]]];
console.log(deeplyNested); // [1, [2, [3, [4, [5]]]]]
</code></pre>
<p><strong>Real-World Examples:</strong></p>
<pre><code class="language-javascript">// Example 1: User groups and members
const groups = [
  ["Raj", "Priya"],
  ["Anil", "Sneha", "Vikram"],
  ["Arjun"]
];

// Example 2: Product categories
const categories = [
  ["Electronics", ["Phones", "Laptops"]],
  ["Clothing", ["Men", "Women"]]
];

// Example 3: Geographic locations
const locations = [
  ["Jaipur", ["Jawahar Nagar", "C-Scheme"]],
  ["Delhi", ["Dwarka", ["Sector 1", "Sector 2"]]],
  ["Mumbai", ["Bandra", "Juhu"]]
];
</code></pre>
<p><strong>Accessing Nested Elements:</strong></p>
<pre><code class="language-javascript">const data = [1, [2, 3], [4, [5, 6]]];

// Access first level
console.log(data[0]); // 1
console.log(data[1]); // [2, 3]
console.log(data[2]); // [4, [5, 6]]

// Access second level
console.log(data[1][0]); // 2
console.log(data[1][1]); // 3
console.log(data[2][1]); // [5, 6]

// Access third level
console.log(data[2][1][0]); // 5
console.log(data[2][1][1]); // 6
</code></pre>
<p><strong>Levels of Nesting:</strong></p>
<pre><code class="language-javascript">// Level 0: No nesting
const level0 = [1, 2, 3];

// Level 1: One level of nesting
const level1 = [1, [2, 3], 4];

// Level 2: Two levels of nesting
const level2 = [1, [2, [3, 4]], 5];

// Level 3: Three levels of nesting
const level3 = [1, [2, [3, [4, 5]]], 6];

// Infinite: Deeply nested (many levels)
const infinite = [1, [2, [3, [4, [5, [6, [7, [8]]]]]]]];
</code></pre>
<hr />
<h2><strong>2. Why Is Array Flattening Useful?</strong></h2>
<p>Flattening arrays is important for several real-world scenarios. Let's understand when and why you need it.</p>
<p><strong>Problem 1: Working with Multiple Data Sources</strong></p>
<pre><code class="language-javascript">// Getting data from multiple APIs
const api1Results = [1, 2, 3];
const api2Results = [4, 5, 6];
const api3Results = [7, 8, 9];

// Combining them creates nested structure
const combined = [api1Results, api2Results, api3Results];
console.log(combined); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

// But we want a flat list for processing
const flattened = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(flattened);
</code></pre>
<p><strong>Problem 2: Processing Hierarchical Data</strong></p>
<pre><code class="language-javascript">// File system structure
const fileSystem = [
  "readme.txt",
  ["src", ["main.js", "utils.js"]],
  ["public", ["index.html", "style.css"]]
];

// To process all files, we need to flatten
// Then we can list all: readme.txt, main.js, utils.js, index.html, style.css
</code></pre>
<p><strong>Problem 3: Simplifying Complex Data</strong></p>
<pre><code class="language-javascript">// User groups data from database
const userGroups = [
  ["Raj", "Priya"],
  ["Anil"],
  ["Sneha", "Vikram", "Arjun"]
];

// For sending emails to all users, we need flat list
const allUsers = ["Raj", "Priya", "Anil", "Sneha", "Vikram", "Arjun"];
</code></pre>
<p><strong>Problem 4: Data Transformation</strong></p>
<pre><code class="language-javascript">// Search results from multiple sources
const searchResults = [
  ["iPhone 12", "iPhone 13", "iPhone 14"],  // From Amazon
  ["iPhone 11", "iPhone 12 Pro"],           // From Flipkart
  ["iPhone X", "iPhone 13"]                 // From Snapdeal
];

// For displaying unified results, flatten first
const allResults = ["iPhone 12", "iPhone 13", "iPhone 14", "iPhone 11", ...];
</code></pre>
<p><strong>Problem 5: Easier Iteration and Processing</strong></p>
<pre><code class="language-javascript">// Nested structure - complex iteration
const nested = [[1, 2], [3, 4], [5, 6]];

for (const subArray of nested) {
  for (const item of subArray) {
    console.log(item);
  }
}

// Flattened - simple iteration
const flat = [1, 2, 3, 4, 5, 6];

for (const item of flat) {
  console.log(item);
}

flat.forEach(item =&gt; console.log(item));
flat.map(item =&gt; item * 2);
</code></pre>
<hr />
<h2><strong>3. Concept of Array Flattening</strong></h2>
<p>Flattening means converting a nested array into a single-level array. Let's visualize this process.</p>
<p><strong>Visual Representation:</strong></p>
<pre><code class="language-plaintext">Before Flattening (Nested):
[1, [2, 3], [4, [5, 6]], 7]
└─ Level 0: [1, 7]
   └─ Level 1: [2, 3], [4, [5, 6]]
      └─ Level 2: [5, 6]

After Flattening (Flat):
[1, 2, 3, 4, 5, 6, 7]
</code></pre>
<p><strong>Step-by-Step Flattening:</strong></p>
<pre><code class="language-javascript">// Original nested array
const original = [1, [2, 3], [4, [5, 6]], 7];

// Step 1: Open the first level
// [1, 2, 3, 4, [5, 6], 7]

// Step 2: Open the second level
// [1, 2, 3, 4, 5, 6, 7]

// Result: Completely flat array
</code></pre>
<p><strong>Different Depths of Flattening:</strong></p>
<pre><code class="language-javascript">const nested = [1, [2, [3, [4, 5]]], 6];

// Flatten 1 level
// Result: [1, 2, [3, [4, 5]], 6]

// Flatten 2 levels
// Result: [1, 2, 3, [4, 5], 6]

// Flatten completely (all levels)
// Result: [1, 2, 3, 4, 5, 6]
</code></pre>
<p><strong>Key Concept: Depth</strong></p>
<pre><code class="language-javascript">// Depth 0 (already flat)
[1, 2, 3, 4, 5];

// Depth 1
[1, [2, 3], 4]; // One level of nesting

// Depth 2
[1, [2, [3, 4]], 5]; // Two levels of nesting

// Depth 3 (or infinity/unlimited)
[1, [2, [3, [4, 5]]]]; // Three or more levels

// When flattening, you specify how many levels to flatten
</code></pre>
<hr />
<h2><strong>4. Different Approaches to Flatten Arrays</strong></h2>
<p>There are many ways to flatten arrays in JavaScript. Let's explore the most common and practical approaches.</p>
<p><strong>Approach 1: Using flat() Method (Modern ES2019)</strong></p>
<p>The <code>flat()</code> method is the simplest and most modern way to flatten arrays.</p>
<pre><code class="language-javascript">// Flatten one level
const nested1 = [1, [2, 3], [4, 5]];
const flat1 = nested1.flat();
console.log(flat1); // [1, 2, 3, 4, 5]

// Flatten two levels
const nested2 = [1, [2, [3, 4]], [5, 6]];
const flat2 = nested2.flat(2);
console.log(flat2); // [1, 2, 3, 4, 5, 6]

// Flatten all levels (use Infinity)
const nested3 = [1, [2, [3, [4, 5]]]];
const flat3 = nested3.flat(Infinity);
console.log(flat3); // [1, 2, 3, 4, 5]

// Default is 1 level
const nested4 = [1, [2, 3], [4, [5, 6]]];
const flat4 = nested4.flat(); // Same as flat(1)
console.log(flat4); // [1, 2, 3, 4, [5, 6]]
</code></pre>
<p><strong>Practical Examples:</strong></p>
<pre><code class="language-javascript">// Example 1: Flatten search results
const searchResults = [
  ["iPhone 12", "iPhone 13"],
  ["Samsung S21"],
  ["Pixel 6"]
];

const allPhones = searchResults.flat();
console.log(allPhones); // ["iPhone 12", "iPhone 13", "Samsung S21", "Pixel 6"]

// Example 2: Combine user groups
const userGroups = [
  ["Raj", "Priya"],
  ["Anil", "Sneha", "Vikram"]
];

const allUsers = userGroups.flat();
console.log(allUsers); // ["Raj", "Priya", "Anil", "Sneha", "Vikram"]

// Example 3: Flatten deeply nested data
const locations = [
  ["India", ["Delhi", ["South Delhi"]]],
  ["USA", ["California"]]
];

const allLocations = locations.flat(Infinity);
console.log(allLocations);
// ["India", "Delhi", "South Delhi", "USA", "California"]
</code></pre>
<p><strong>Approach 2: Using flatMap()</strong></p>
<p><code>flatMap()</code> combines <code>map()</code> and <code>flat()</code> in one operation.</p>
<pre><code class="language-javascript">const numbers = [1, 2, 3, 4, 5];

// Using map and then flat
const result1 = numbers.map(n =&gt; [n, n * 2]).flat();
console.log(result1); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

// Using flatMap (more efficient)
const result2 = numbers.flatMap(n =&gt; [n, n * 2]);
console.log(result2); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

// Both produce same result, but flatMap is cleaner
</code></pre>
<p><strong>More flatMap Examples:</strong></p>
<pre><code class="language-javascript">// Example 1: Duplicate each item
const items = ["a", "b", "c"];
const duplicated = items.flatMap(item =&gt; [item, item]);
console.log(duplicated); // ["a", "a", "b", "b", "c", "c"]

// Example 2: Filter and transform simultaneously
const products = [
  { name: "Laptop", inStock: true },
  { name: "Phone", inStock: false },
  { name: "Tablet", inStock: true }
];

const stockedProducts = products.flatMap(product =&gt;
  product.inStock ? [product.name] : []
);
console.log(stockedProducts); // ["Laptop", "Tablet"]

// Example 3: Expand each item
const words = ["hello", "world"];
const chars = words.flatMap(word =&gt; word.split(""));
console.log(chars);
// ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]
</code></pre>
<p><strong>Approach 3: Using Spread Operator</strong></p>
<p>The spread operator can flatten one level at a time.</p>
<pre><code class="language-javascript">// Flatten one level
const nested = [1, [2, 3], [4, 5]];

// Using spread multiple times
const flat1 = [].concat(...nested);
console.log(flat1); // [1, 2, 3, 4, 5]

// Or with spread in array literal
const flat2 = [...nested[0], ...nested[1], ...nested[2]];
console.log(flat2); // Works but not scalable

// For multiple levels, need to repeat
const nested2 = [1, [2, [3, 4]], 5];
const flat3 = [].concat(...nested2); // [1, 2, [3, 4], 5] - Only one level
</code></pre>
<p><strong>Approach 4: Using Recursion</strong></p>
<p>Recursion is a traditional approach that works for any depth.</p>
<pre><code class="language-javascript">// Recursive flattening function
function flattenRecursive(arr) {
  const result = [];
  
  for (const item of arr) {
    if (Array.isArray(item)) {
      // If item is an array, recursively flatten it
      result.push(...flattenRecursive(item));
    } else {
      // If item is not an array, just add it
      result.push(item);
    }
  }
  
  return result;
}

// Test it
const nested = [1, [2, [3, [4, 5]]], 6];
const flattened = flattenRecursive(nested);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
</code></pre>
<p><strong>Another Recursive Approach:</strong></p>
<pre><code class="language-javascript">function flattenSimple(arr) {
  return arr.reduce((acc, val) =&gt;
    Array.isArray(val) ? acc.concat(flattenSimple(val)) : acc.concat(val),
    []
  );
}

const nested = [1, [2, [3, 4]], 5];
console.log(flattenSimple(nested)); // [1, 2, 3, 4, 5]
</code></pre>
<p><strong>Approach 5: Using forEach with Recursion</strong></p>
<pre><code class="language-javascript">function flattenWithForeach(arr) {
  const result = [];
  
  arr.forEach(item =&gt; {
    if (Array.isArray(item)) {
      result.push(...flattenWithForeach(item));
    } else {
      result.push(item);
    }
  });
  
  return result;
}

const nested = [1, [2, [3, 4]], 5];
console.log(flattenWithForeach(nested)); // [1, 2, 3, 4, 5]
</code></pre>
<p><strong>Approach 6: Flatten with Depth Limit</strong></p>
<pre><code class="language-javascript">function flattenWithDepth(arr, depth = 1) {
  const result = [];
  
  for (const item of arr) {
    if (Array.isArray(item) &amp;&amp; depth &gt; 0) {
      result.push(...flattenWithDepth(item, depth - 1));
    } else {
      result.push(item);
    }
  }
  
  return result;
}

const nested = [1, [2, [3, [4, 5]]], 6];

console.log(flattenWithDepth(nested, 1)); // [1, 2, [3, [4, 5]], 6]
console.log(flattenWithDepth(nested, 2)); // [1, 2, 3, [4, 5], 6]
console.log(flattenWithDepth(nested, 3)); // [1, 2, 3, 4, 5, 6]
console.log(flattenWithDepth(nested, Infinity)); // [1, 2, 3, 4, 5, 6]
</code></pre>
<p><strong>Comparison of Approaches:</strong></p>
<table>
<thead>
<tr>
<th>Approach</th>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody><tr>
<td>flat()</td>
<td>Simple, modern, readable</td>
<td>Only works in modern browsers</td>
</tr>
<tr>
<td>flatMap()</td>
<td>Combines map and flat</td>
<td>Limited use cases</td>
</tr>
<tr>
<td>Spread + concat</td>
<td>Simple for one level</td>
<td>Only flattens one level</td>
</tr>
<tr>
<td>Recursion</td>
<td>Works for any depth</td>
<td>Can be slower for large arrays</td>
</tr>
<tr>
<td>Custom function</td>
<td>Full control, reusable</td>
<td>More code to write</td>
</tr>
</tbody></table>
<hr />
<h2><strong>5. Real-World Scenarios and Interview Questions</strong></h2>
<p><strong>Scenario 1: Flatten Search Results (Common Interview)</strong></p>
<pre><code class="language-javascript">// Problem: Combine search results from multiple platforms
const searchResults = {
  amazon: ["iPhone 12", "iPhone 13"],
  flipkart: ["Samsung S21", "Samsung S22"],
  myntra: ["OnePlus 9"]
};

// Solution
function getAllSearchResults(results) {
  return Object.values(results).flat();
}

const allProducts = getAllSearchResults(searchResults);
console.log(allProducts);
// ["iPhone 12", "iPhone 13", "Samsung S21", "Samsung S22", "OnePlus 9"]
</code></pre>
<p><strong>Scenario 2: Flatten Comment Threads (Common in Social Media)</strong></p>
<pre><code class="language-javascript">// Problem: Flatten nested comment threads
const comments = [
  {
    id: 1,
    text: "Great post!",
    replies: [
      { id: 2, text: "Thanks!" },
      { id: 3, text: "I agree" }
    ]
  },
  {
    id: 4,
    text: "Not sure about this",
    replies: [
      { id: 5, text: "Why?" }
    ]
  }
];

// Solution
function getAllComments(comments) {
  return comments.flatMap(comment =&gt; [
    comment,
    ...getAllComments(comment.replies || [])
  ]);
}

const allComments = getAllComments(comments);
console.log(allComments.map(c =&gt; c.text));
// ["Great post!", "Thanks!", "I agree", "Not sure about this", "Why?"]
</code></pre>
<p><strong>Scenario 3: Flatten Permissions (Common in Auth Systems)</strong></p>
<pre><code class="language-javascript">// Problem: User has roles with permissions, flatten to get all permissions
const user = {
  id: 1,
  name: "Ashish",
  roles: [
    {
      name: "Admin",
      permissions: ["read", "write", "delete"]
    },
    {
      name: "Editor",
      permissions: ["read", "write"]
    }
  ]
};

// Solution
function getAllPermissions(user) {
  return user.roles
    .map(role =&gt; role.permissions)
    .flat();
}

const permissions = getAllPermissions(user);
console.log(permissions); // ["read", "write", "delete", "read", "write"]

// Remove duplicates
const uniquePermissions = [...new Set(permissions)];
console.log(uniquePermissions); // ["read", "write", "delete"]
</code></pre>
<p><strong>Scenario 4: Flatten Tree Structure (Common in DOM and Data Trees)</strong></p>
<pre><code class="language-javascript">// Problem: Flatten a tree structure
const organizationChart = {
  name: "CEO",
  employees: [
    {
      name: "Manager 1",
      employees: [
        { name: "Developer 1" },
        { name: "Developer 2" }
      ]
    },
    {
      name: "Manager 2",
      employees: [
        { name: "Designer 1" }
      ]
    }
  ]
};

// Solution
function flattenTree(node) {
  const result = [node.name];
  
  if (node.employees &amp;&amp; node.employees.length &gt; 0) {
    node.employees.forEach(employee =&gt; {
      result.push(...flattenTree(employee));
    });
  }
  
  return result;
}

const allEmployees = flattenTree(organizationChart);
console.log(allEmployees);
// ["CEO", "Manager 1", "Developer 1", "Developer 2", "Manager 2", "Designer 1"]
</code></pre>
<p><strong>Scenario 5: Interview Question - Flatten Unknown Depth</strong></p>
<pre><code class="language-javascript">// Problem: Flatten an array of unknown depth
function flattenAny(arr) {
  return arr.reduce((flat, item) =&gt;
    Array.isArray(item) ? flat.concat(flattenAny(item)) : flat.concat(item),
    []
  );
}

// Test
const test1 = [1, [2, 3], [4, [5, 6]]];
console.log(flattenAny(test1)); // [1, 2, 3, 4, 5, 6]

const test2 = [[[1]], [[2]], [[[3]]]];
console.log(flattenAny(test2)); // [1, 2, 3]

const test3 = [1, [2, [3, [4, [5]]]]];
console.log(flattenAny(test3)); // [1, 2, 3, 4, 5]
</code></pre>
<p><strong>Scenario 6: Flatten with Filtering</strong></p>
<pre><code class="language-javascript">// Problem: Flatten and filter in one operation
const data = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

// Get all numbers greater than 4
const filtered = data.flatMap(subArray =&gt;
  subArray.filter(num =&gt; num &gt; 4)
);

console.log(filtered); // [5, 6, 7, 8, 9]
</code></pre>
<p><strong>Scenario 7: Common Interview Trick - What if Array Contains null?</strong></p>
<pre><code class="language-javascript">const nested = [1, [2, null, 3], [4, undefined, 5]];

// flat() keeps null and undefined
console.log(nested.flat()); // [1, 2, null, 3, 4, undefined, 5]

// If you want to remove them
const cleaned = nested.flat().filter(item =&gt; item != null);
console.log(cleaned); // [1, 2, 3, 4, 5]

// Or during flattening
function flattenClean(arr) {
  return arr.reduce((flat, item) =&gt;
    Array.isArray(item) 
      ? flat.concat(flattenClean(item)) 
      : item != null ? flat.concat(item) : flat,
    []
  );
}

console.log(flattenClean(nested)); // [1, 2, 3, 4, 5]
</code></pre>
<hr />
<h2><strong>6. Performance Comparison</strong></h2>
<p>Let's compare different flattening approaches for performance.</p>
<pre><code class="language-javascript">// Create a test array
const createTestArray = (levels) =&gt; {
  if (levels === 0) return 1;
  return [createTestArray(levels - 1), createTestArray(levels - 1)];
};

const testArray = createTestArray(5);

// Test 1: Using flat()
console.time("flat()");
const result1 = testArray.flat(Infinity);
console.timeEnd("flat()");

// Test 2: Using recursive function
function flatRecursive(arr) {
  return arr.reduce((flat, item) =&gt;
    Array.isArray(item) ? flat.concat(flatRecursive(item)) : flat.concat(item),
    []
  );
}

console.time("Recursive");
const result2 = flatRecursive(testArray);
console.timeEnd("Recursive");

// Modern flat() is usually faster because it's optimized by the engine
</code></pre>
<p><strong>Performance Tips:</strong></p>
<pre><code class="language-javascript">// Good: Use flat() when available - it's optimized
const flat1 = array.flat(Infinity);

// Avoid: concat with spread in loop - creates many intermediate arrays
let result = [];
for (const item of array) {
  result = [...result, ...item]; // Inefficient
}

// Better: Use reduce
const flat2 = array.reduce((flat, item) =&gt;
  flat.concat(item), []
);

// Best: Use flat()
const flat3 = array.flat();
</code></pre>
<hr />
<h2><strong>7. Visual Flattening Process</strong></h2>
<p><strong>Step-by-Step Visualization:</strong></p>
<pre><code class="language-plaintext">Original Array:
[1, [2, 3], [4, [5, 6]], 7]

Level 0 (input):
┌─ 1
├─ [2, 3]
├─ [4, [5, 6]]
└─ 7

First Pass (flatten 1 level):
┌─ 1
├─ 2
├─ 3
├─ 4
├─ [5, 6]
└─ 7

Second Pass (flatten 2 levels):
┌─ 1
├─ 2
├─ 3
├─ 4
├─ 5
├─ 6
└─ 7

Result:
[1, 2, 3, 4, 5, 6, 7]
</code></pre>
<p><strong>Recursion Visualization:</strong></p>
<pre><code class="language-plaintext">flattenRecursive([1, [2, [3, 4]], 5])
├─ Process 1 → add to result
├─ flattenRecursive([2, [3, 4]])
│  ├─ Process 2 → add to result
│  └─ flattenRecursive([3, 4])
│     ├─ Process 3 → add to result
│     └─ Process 4 → add to result
└─ Process 5 → add to result

Result: [1, 2, 3, 4, 5]
</code></pre>
<hr />
<h2><strong>8. Edge Cases to Handle</strong></h2>
<p><strong>Edge Case 1: Empty Arrays</strong></p>
<pre><code class="language-javascript">const empty = [];
console.log(empty.flat()); // []

const withEmpty = [1, [], 2, []];
console.log(withEmpty.flat()); // [1, 2]
</code></pre>
<p><strong>Edge Case 2: Non-Array Elements</strong></p>
<pre><code class="language-javascript">const mixed = [1, "hello", true, { key: "value" }];
console.log(mixed.flat()); // [1, "hello", true, { key: "value" }]
// Already flat, nothing changes
</code></pre>
<p><strong>Edge Case 3: Null and Undefined</strong></p>
<pre><code class="language-javascript">const withNulls = [1, [null, 2], [undefined, 3]];
console.log(withNulls.flat()); // [1, null, 2, undefined, 3]
// null and undefined are preserved
</code></pre>
<p><strong>Edge Case 4: Very Deep Nesting</strong></p>
<pre><code class="language-javascript">const veryDeep = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]];

console.log(veryDeep.flat()); // Only flattens 1 level
// [1, 2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]

console.log(veryDeep.flat(Infinity)); // Flattens all levels
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
</code></pre>
<p><strong>Edge Case 5: Sparse Arrays (Arrays with Holes)</strong></p>
<pre><code class="language-javascript">const sparse = [1, , 3]; // Note the missing element
console.log(sparse.length); // 3
console.log(sparse); // [1, &lt;empty&gt;, 3]

const nested = [1, [, 2], 3];
console.log(nested.flat());
// [1, 2, 3] - Empty slots are removed
</code></pre>
<hr />
<h2><strong>9. Best Practices and Recommendations</strong></h2>
<p><strong>Best Practice 1: Use flat() for Simple Cases</strong></p>
<pre><code class="language-javascript">// Good: Clear and modern
const result = array.flat();
const deepResult = array.flat(Infinity);

// Avoid if not needed: Making it complex
const manual = array.reduce((flat, item) =&gt;
  Array.isArray(item) ? flat.concat(item) : flat.concat(item),
  []
);
</code></pre>
<p><strong>Best Practice 2: Use flatMap() When Transforming</strong></p>
<pre><code class="language-javascript">// Good: flatMap combines operations
const doubled = [1, 2, 3].flatMap(n =&gt; [n, n * 2]);

// Not as good: Separate map and flat
const doubled2 = [1, 2, 3].map(n =&gt; [n, n * 2]).flat();
</code></pre>
<p><strong>Best Practice 3: Specify Depth Explicitly</strong></p>
<pre><code class="language-javascript">// Good: Clear what you're doing
const flat1 = array.flat(1); // Exactly 1 level
const flat2 = array.flat(2); // Exactly 2 levels

// Avoid: Magic number without context
const flat3 = array.flat(5); // Why 5? Not clear
</code></pre>
<p><strong>Best Practice 4: Handle Edge Cases</strong></p>
<pre><code class="language-javascript">// Good: Consider what data you're receiving
function flattenData(arr) {
  if (!Array.isArray(arr)) {
    return [];
  }
  
  return arr.flat(Infinity).filter(item =&gt; item != null);
}

// Test with various inputs
console.log(flattenData(null)); // []
console.log(flattenData([1, null, 2])); // [1, 2]
</code></pre>
<p><strong>Best Practice 5: Consider Performance</strong></p>
<pre><code class="language-javascript">// For large deeply nested arrays, be careful
const huge = generateLargeNestedArray();

// Good: Don't over-flatten
const result1 = huge.flat(2); // Only flatten 2 levels

// Avoid: Always flattening completely might be slow
const result2 = huge.flat(Infinity); // Might be slow
</code></pre>
<hr />
<h2><strong>10. Complete Examples</strong></h2>
<p><strong>Example 1: Database Query Results</strong></p>
<pre><code class="language-javascript">// Simulate API returning multiple result sets
const apiResponses = [
  [
    { id: 1, name: "Raj" },
    { id: 2, name: "Priya" }
  ],
  [
    { id: 3, name: "Anil" },
    { id: 4, name: "Sneha" }
  ]
];

// Flatten to get all users
const allUsers = apiResponses.flat();
console.log(allUsers);
// [
//   { id: 1, name: "Raj" },
//   { id: 2, name: "Priya" },
//   { id: 3, name: "Anil" },
//   { id: 4, name: "Sneha" }
// ]

// Further processing
const names = allUsers.map(user =&gt; user.name);
console.log(names); // ["Raj", "Priya", "Anil", "Sneha"]
</code></pre>
<p><strong>Example 2: File System Listing</strong></p>
<pre><code class="language-javascript">// Directory structure
const directories = {
  src: [
    "main.js",
    "utils.js",
    { components: ["Button.js", "Card.js"] }
  ],
  public: ["index.html", "favicon.ico"],
  tests: ["main.test.js", "utils.test.js"]
};

// Get all files (simple case)
function getAllFiles(obj) {
  return Object.values(obj).flat();
}

console.log(getAllFiles(directories));
// ["main.js", "utils.js", {components: [...]}, "index.html", "favicon.ico", "main.test.js", "utils.test.js"]
</code></pre>
<p><strong>Example 3: Combine Multiple Lists</strong></p>
<pre><code class="language-javascript">// Different user categories
const activeUsers = ["Raj", "Priya"];
const inactiveUsers = ["Anil", "Sneha"];
const newUsers = ["Vikram", "Arjun"];

// Combine all
const allUserLists = [activeUsers, inactiveUsers, newUsers];
const allUsers = allUserLists.flat();

console.log(allUsers);
// ["Raj", "Priya", "Anil", "Sneha", "Vikram", "Arjun"]

// Count total
console.log(allUsers.length); // 6
</code></pre>
<hr />
<h2><strong>Conclusion</strong></h2>
<p>Array flattening is a fundamental technique in JavaScript that helps you work with nested data structures. Here's what we've learned:</p>
<p><strong>Key Concepts:</strong></p>
<ol>
<li><p>Nested arrays contain other arrays as elements</p>
</li>
<li><p>Flattening converts nested structures to single-level arrays</p>
</li>
<li><p>Different approaches suit different scenarios</p>
</li>
</ol>
<p><strong>Main Methods:</strong></p>
<ul>
<li><p><code>flat()</code>: Simplest and most modern</p>
</li>
<li><p><code>flatMap()</code>: Best when combining map and flatten</p>
</li>
<li><p>Recursion: Works for any depth</p>
</li>
<li><p>Spread operator: Good for one level</p>
</li>
</ul>
<p><strong>When to Flatten:</strong></p>
<ul>
<li><p>Combining data from multiple sources</p>
</li>
<li><p>Processing hierarchical data</p>
</li>
<li><p>Simplifying for iteration and processing</p>
</li>
<li><p>Preparing data for display or storage</p>
</li>
</ul>
<p><strong>Best Practices:</strong></p>
<ul>
<li><p>Use <code>flat()</code> for modern code</p>
</li>
<li><p>Specify depth explicitly</p>
</li>
<li><p>Handle edge cases (null, undefined, sparse arrays)</p>
</li>
<li><p>Consider performance for large arrays</p>
</li>
<li><p>Use <code>flatMap()</code> when transforming</p>
</li>
</ul>
<p>Master array flattening, and you'll handle hierarchical and nested data with confidence. Whether it's in interviews, API responses, or database queries, this skill is invaluable for professional JavaScript development.</p>
]]></content:encoded></item><item><title><![CDATA[Map and Set in JavaScript: When and Why to Use Them]]></title><description><![CDATA[Introduction
For years, JavaScript developers relied on objects for storing key-value pairs and arrays for storing collections of values. While these work well for many situations, they have limitatio]]></description><link>https://blog.ashish.pro/map-and-set-in-javascript</link><guid isPermaLink="true">https://blog.ashish.pro/map-and-set-in-javascript</guid><dc:creator><![CDATA[Ashish Singodiya]]></dc:creator><pubDate>Mon, 23 Mar 2026 05:00:00 GMT</pubDate><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2>
<p>For years, JavaScript developers relied on objects for storing key-value pairs and arrays for storing collections of values. While these work well for many situations, they have limitations. JavaScript introduced Map and Set to provide better solutions for specific use cases. Maps are perfect for storing key-value pairs where keys can be any type, and Sets are ideal for storing unique values. In this blog, we'll explore Map and Set in detail, understand their advantages over traditional objects and arrays, and learn when to use each one.</p>
<hr />
<h2><strong>1. What is Map?</strong></h2>
<p>A Map is a collection of key-value pairs where both keys and values can be of any data type. It's similar to an object, but more flexible and with better performance for certain operations.</p>
<p><strong>Basic Concept:</strong></p>
<pre><code class="language-javascript">// Creating an empty Map
const emptyMap = new Map();

// Creating a Map with initial values
const userMap = new Map([
  ["user1", "Ashish"],
  ["user2", "Priya"],
  ["user3", "Anil"]
]);

console.log(userMap);
// Map(3) {
//   'user1' =&gt; 'Ashish',
//   'user2' =&gt; 'Priya',
//   'user3' =&gt; 'Anil'
// }
</code></pre>
<p>Notice the <code>=&gt;</code> arrow symbol. This is how Maps display key-value pairs.</p>
<p><strong>Adding Items to a Map:</strong></p>
<pre><code class="language-javascript">const productMap = new Map();

// Use .set() to add key-value pairs
productMap.set("laptop", 50000);
productMap.set("phone", 30000);
productMap.set("tablet", 20000);

console.log(productMap);
// Map(3) {
//   'laptop' =&gt; 50000,
//   'phone' =&gt; 30000,
//   'tablet' =&gt; 20000
// }
</code></pre>
<p><strong>Accessing Values:</strong></p>
<pre><code class="language-javascript">const priceMap = new Map([
  ["apple", 100],
  ["banana", 50],
  ["orange", 80]
]);

// Use .get() to retrieve values
console.log(priceMap.get("apple")); // 100
console.log(priceMap.get("banana")); // 50
console.log(priceMap.get("orange")); // 80

// If key doesn't exist, returns undefined
console.log(priceMap.get("mango")); // undefined
</code></pre>
<p><strong>Checking if Key Exists:</strong></p>
<pre><code class="language-javascript">const userMap = new Map([
  ["user1", "Raj"],
  ["user2", "Priya"]
]);

// Use .has() to check if key exists
console.log(userMap.has("user1")); // true
console.log(userMap.has("user3")); // false

if (userMap.has("user1")) {
  console.log(userMap.get("user1")); // Raj
}
</code></pre>
<p><strong>Deleting and Clearing:</strong></p>
<pre><code class="language-javascript">const map = new Map([
  ["a", 1],
  ["b", 2],
  ["c", 3]
]);

// Remove a specific key
map.delete("b");
console.log(map.size); // 2

// Clear entire map
map.clear();
console.log(map.size); // 0
</code></pre>
<p><strong>Getting Map Size:</strong></p>
<pre><code class="language-javascript">const map = new Map();
console.log(map.size); // 0

map.set("key1", "value1");
console.log(map.size); // 1

map.set("key2", "value2");
console.log(map.size); // 2
</code></pre>
<p><strong>Key Property: Keys Can Be Any Type</strong></p>
<p>This is a huge advantage of Maps over objects!</p>
<pre><code class="language-javascript">const map = new Map();

// String key
map.set("name", "Vikram");

// Number key
map.set(1, "One");

// Boolean key
map.set(true, "Yes");

// Object as key
const userObj = { id: 1 };
map.set(userObj, "User object as key");

// Function as key
function myFunc() {}
map.set(myFunc, "Function as key");

console.log(map.get("name")); // "Vikram"
console.log(map.get(1)); // "One"
console.log(map.get(true)); // "Yes"
console.log(map.get(userObj)); // "User object as key"
console.log(map.get(myFunc)); // "Function as key"
</code></pre>
<p>With objects, all keys are converted to strings. With Map, keys stay as their original type.</p>
<p><strong>Iterating Over a Map:</strong></p>
<pre><code class="language-javascript">const scoreMap = new Map([
  ["Raj", 85],
  ["Priya", 92],
  ["Anil", 78]
]);

// Method 1: Using forEach
scoreMap.forEach((value, key) =&gt; {
  console.log(`\({key}: \){value}`);
});
// Output:
// Raj: 85
// Priya: 92
// Anil: 78

// Method 2: Using for...of with entries()
for (const [key, value] of scoreMap.entries()) {
  console.log(`\({key}: \){value}`);
}

// Method 3: Get only keys
for (const key of scoreMap.keys()) {
  console.log(key);
}

// Method 4: Get only values
for (const value of scoreMap.values()) {
  console.log(value);
}
</code></pre>
<hr />
<h2><strong>2. What is Set?</strong></h2>
<p>A Set is a collection of unique values. Any value in a Set appears only once, even if you try to add it multiple times.</p>
<p><strong>Basic Concept:</strong></p>
<pre><code class="language-javascript">// Creating an empty Set
const emptySet = new Set();

// Creating a Set with initial values
const numberSet = new Set([1, 2, 3, 4, 5]);

console.log(numberSet);
// Set(5) { 1, 2, 3, 4, 5 }
</code></pre>
<p><strong>Adding Items to a Set:</strong></p>
<pre><code class="language-javascript">const colorSet = new Set();

// Use .add() to add values
colorSet.add("Red");
colorSet.add("Green");
colorSet.add("Blue");

console.log(colorSet);
// Set(3) { 'Red', 'Green', 'Blue' }

// Try to add a duplicate
colorSet.add("Red"); // No error, but duplicate is ignored

console.log(colorSet);
// Set(3) { 'Red', 'Green', 'Blue' } - Still 3 items!
</code></pre>
<p>The key property of Set is uniqueness. Duplicates are automatically ignored.</p>
<p><strong>Checking if Value Exists:</strong></p>
<pre><code class="language-javascript">const fruits = new Set(["Apple", "Banana", "Orange"]);

// Use .has() to check if value exists
console.log(fruits.has("Apple")); // true
console.log(fruits.has("Mango")); // false

if (fruits.has("Apple")) {
  console.log("Apple is in the set");
}
</code></pre>
<p><strong>Deleting and Clearing:</strong></p>
<pre><code class="language-javascript">const set = new Set([1, 2, 3, 4, 5]);

// Remove a specific value
set.delete(3);
console.log(set.size); // 4

// Clear entire set
set.clear();
console.log(set.size); // 0
</code></pre>
<p><strong>Getting Set Size:</strong></p>
<pre><code class="language-javascript">const set = new Set();
console.log(set.size); // 0

set.add("item1");
console.log(set.size); // 1

set.add("item2");
set.add("item3");
console.log(set.size); // 3
</code></pre>
<p><strong>Set Can Store Any Type:</strong></p>
<pre><code class="language-javascript">const mixedSet = new Set();

// String
mixedSet.add("text");

// Number
mixedSet.add(42);

// Boolean
mixedSet.add(true);

// Object
mixedSet.add({ name: "Raj" });

// Array
mixedSet.add([1, 2, 3]);

// Function
mixedSet.add(function() {});

console.log(mixedSet.size); // 6
</code></pre>
<p><strong>Iterating Over a Set:</strong></p>
<pre><code class="language-javascript">const languageSet = new Set(["JavaScript", "Python", "Java", "Go"]);

// Method 1: Using forEach
languageSet.forEach(value =&gt; {
  console.log(value);
});
// Output:
// JavaScript
// Python
// Java
// Go

// Method 2: Using for...of
for (const value of languageSet) {
  console.log(value);
}

// Method 3: Convert to Array
const languageArray = Array.from(languageSet);
console.log(languageArray);
</code></pre>
<p><strong>Practical Use: Removing Duplicates</strong></p>
<pre><code class="language-javascript">// Array with duplicates
const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5];

// Convert to Set (duplicates removed)
const uniqueNumbers = new Set(numbers);

// Convert back to Array if needed
const uniqueArray = Array.from(uniqueNumbers);

console.log(uniqueArray); // [1, 2, 3, 4, 5]
</code></pre>
<hr />
<h2><strong>3. Difference Between Map and Object</strong></h2>
<p>While both store key-value pairs, Map and Object have important differences.</p>
<p><strong>Difference 1: Key Types</strong></p>
<pre><code class="language-javascript">// OBJECT: Keys are always converted to strings
const obj = {};
obj[1] = "one";
obj[true] = "yes";
obj[{ id: 1 }] = "object";

console.log(Object.keys(obj)); // ["1", "true", "[object Object]"]
// All keys are strings!

// MAP: Keys keep their original type
const map = new Map();
map.set(1, "one");
map.set(true, "yes");
map.set({ id: 1 }, "object");

console.log(map.size); // 3
console.log(map.get(1)); // "one"
console.log(map.get(true)); // "yes"
</code></pre>
<p><strong>Difference 2: Method Availability</strong></p>
<pre><code class="language-javascript">// OBJECT: Limited methods
const obj = { name: "Raj", age: 25 };
console.log(obj.name); // Raj
console.log(Object.keys(obj)); // ["name", "age"]
console.log(obj.hasOwnProperty("name")); // true
delete obj.age; // Delete property

// MAP: Better methods for key-value operations
const map = new Map([["name", "Raj"], ["age", 25]]);
console.log(map.get("name")); // Raj
console.log(map.has("name")); // true
map.delete("age"); // Delete entry
map.clear(); // Clear all entries
</code></pre>
<p><strong>Difference 3: Size Property</strong></p>
<pre><code class="language-javascript">// OBJECT: No built-in size
const obj = { a: 1, b: 2, c: 3 };
console.log(obj.size); // undefined
// Have to use Object.keys(obj).length
console.log(Object.keys(obj).length); // 3

// MAP: Built-in size property
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
console.log(map.size); // 3 - Much easier!
</code></pre>
<p><strong>Difference 4: Iteration</strong></p>
<pre><code class="language-javascript">// OBJECT: Not directly iterable
const obj = { name: "Priya", city: "Delhi" };
// for (const value of obj) { } // ERROR!

// Have to convert to array first
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

// MAP: Directly iterable
const map = new Map([["name", "Priya"], ["city", "Delhi"]]);
// Works directly!
for (const [key, value] of map) {
  console.log(key, value);
}
</code></pre>
<p><strong>Difference 5: Performance</strong></p>
<pre><code class="language-javascript">// MAP is generally faster for frequent add/delete operations
const map = new Map();

// Adding and deleting is optimized
map.set("key1", "value1");
map.delete("key1");
map.set("key2", "value2");

// OBJECT operations can be slower with many properties
const obj = {};
obj.key1 = "value1";
delete obj.key1;
obj.key2 = "value2";
</code></pre>
<p><strong>Comparison Table:</strong></p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Object</th>
<th>Map</th>
</tr>
</thead>
<tbody><tr>
<td>Key Types</td>
<td>Strings only</td>
<td>Any type</td>
</tr>
<tr>
<td>Size</td>
<td>.length or Object.keys()</td>
<td>.size property</td>
</tr>
<tr>
<td>Iteration</td>
<td>Requires Object.entries()</td>
<td>Direct iteration</td>
</tr>
<tr>
<td>Performance</td>
<td>Good for small data</td>
<td>Better for frequent changes</td>
</tr>
<tr>
<td>Methods</td>
<td>Limited</td>
<td>has(), get(), delete(), clear()</td>
</tr>
<tr>
<td>Inheritance</td>
<td>Prototypes matter</td>
<td>None</td>
</tr>
</tbody></table>
<p><strong>When to Use:</strong></p>
<pre><code class="language-javascript">// Use OBJECT when:
// - Keys are strings
// - You want simple property access
// - You need prototype features

const user = {
  name: "Arun",
  email: "arun@example.com",
  age: 26
};

// Use MAP when:
// - Keys might not be strings
// - You need frequent add/delete
// - You need guaranteed size property

const urlCache = new Map();
urlCache.set(new URL("https://example.com"), "cached data");
</code></pre>
<hr />
<h2><strong>4. Difference Between Set and Array</strong></h2>
<p>While both store collections of values, Set and Array have different characteristics.</p>
<p><strong>Difference 1: Uniqueness</strong></p>
<pre><code class="language-javascript">// ARRAY: Allows duplicates
const arr = [1, 2, 2, 3, 3, 3, 4];
console.log(arr.length); // 7 - Includes duplicates

// SET: Only unique values
const set = new Set([1, 2, 2, 3, 3, 3, 4]);
console.log(set.size); // 4 - Duplicates removed automatically
</code></pre>
<p><strong>Difference 2: Methods Available</strong></p>
<pre><code class="language-javascript">// ARRAY: Lots of array methods
const arr = [1, 2, 3, 4, 5];
arr.push(6); // Add
arr.pop(); // Remove last
arr.map(x =&gt; x * 2); // Transform
arr.filter(x =&gt; x &gt; 2); // Filter
arr.find(x =&gt; x === 3); // Find
arr.indexOf(3); // Get index

// SET: Simpler, specific methods
const set = new Set([1, 2, 3, 4, 5]);
set.add(6); // Add
set.delete(5); // Delete specific value
set.has(3); // Check existence
set.clear(); // Clear all
// No filter, map, find, etc. built-in
</code></pre>
<p><strong>Difference 3: Access Pattern</strong></p>
<pre><code class="language-javascript">// ARRAY: Access by index
const arr = ["a", "b", "c", "d"];
console.log(arr[0]); // "a"
console.log(arr[2]); // "c"
console.log(arr.length); // 4

// SET: No index access
const set = new Set(["a", "b", "c", "d"]);
console.log(set[0]); // undefined - No index access!
console.log(set.size); // 4

// To get values, must iterate
for (const value of set) {
  console.log(value);
}
</code></pre>
<p><strong>Difference 4: Order</strong></p>
<pre><code class="language-javascript">// ARRAY: Maintains insertion order
const arr = [3, 1, 4, 1, 5, 9];
console.log(arr); // [3, 1, 4, 1, 5, 9] - Order preserved

// SET: Also maintains insertion order (in modern JS)
const set = new Set([3, 1, 4, 1, 5, 9]);
for (const value of set) {
  console.log(value); // 3, 1, 4, 5, 9 (order preserved, duplicates removed)
}
</code></pre>
<p><strong>Difference 5: Performance for Contains Check</strong></p>
<pre><code class="language-javascript">// ARRAY: O(n) complexity - must search through all items
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(arr.includes(5)); // true - Has to check each item

// SET: O(1) complexity - instant lookup
const set = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log(set.has(5)); // true - Instant lookup
</code></pre>
<p>For large collections, Set is much faster for checking existence.</p>
<p><strong>Comparison Table:</strong></p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Array</th>
<th>Set</th>
</tr>
</thead>
<tbody><tr>
<td>Duplicates</td>
<td>Allows</td>
<td>Rejects</td>
</tr>
<tr>
<td>Index Access</td>
<td>Yes (arr[0])</td>
<td>No</td>
</tr>
<tr>
<td>Methods</td>
<td>Many (map, filter, etc.)</td>
<td>Few (add, delete, etc.)</td>
</tr>
<tr>
<td>Check Existence</td>
<td>.includes() - slow</td>
<td>.has() - fast</td>
</tr>
<tr>
<td>Size</td>
<td>.length</td>
<td>.size</td>
</tr>
<tr>
<td>Order</td>
<td>Preserved</td>
<td>Preserved</td>
</tr>
</tbody></table>
<p><strong>When to Use:</strong></p>
<pre><code class="language-javascript">// Use ARRAY when:
// - Order matters
// - You need to transform values
// - You need index access
// - Duplicates are meaningful

const scores = [85, 92, 78, 95, 92]; // Score list where duplicates matter

const names = ["Raj", "Priya", "Anil"]; // Ordered list

// Use SET when:
// - You need unique values
// - You frequently check for existence
// - You don't need index access
// - Duplicates should be eliminated

const uniqueTags = new Set(["javascript", "react", "javascript"]);
console.log(uniqueTags.size); // 2 - Duplicates removed

const visitedUrls = new Set();
if (!visitedUrls.has(url)) {
  visitedUrls.add(url); // Fast lookup and addition
}
</code></pre>
<hr />
<h2><strong>5. When to Use Map and Set</strong></h2>
<p>Understanding when to use Map and Set instead of objects and arrays is crucial for writing efficient code.</p>
<p><strong>Use Map When:</strong></p>
<p><strong>1. Keys Are Not Strings</strong></p>
<pre><code class="language-javascript">// BAD: Using object with non-string keys
const obj = {};
obj[1] = "one";
obj[2] = "two";
const key = 1;
console.log(obj[key]); // Works but key is converted to string

// GOOD: Using Map with non-string keys
const map = new Map();
map.set(1, "one");
map.set(2, "two");
const key = 1;
console.log(map.get(key)); // Perfect!
</code></pre>
<p><strong>2. Frequent Add/Delete Operations</strong></p>
<pre><code class="language-javascript">// Cache with frequent updates
const userCache = new Map();

function cacheUser(id, userData) {
  userCache.set(id, userData);
}

function getCachedUser(id) {
  return userCache.get(id);
}

function clearExpiredUser(id) {
  userCache.delete(id);
}
</code></pre>
<p><strong>3. You Need a Built-in Size</strong></p>
<pre><code class="language-javascript">// Tracking connected clients
const connectedClients = new Map();

function addClient(clientId, clientData) {
  connectedClients.set(clientId, clientData);
  console.log(`Connected clients: ${connectedClients.size}`);
}
</code></pre>
<p><strong>4. Using Objects as Keys</strong></p>
<pre><code class="language-javascript">// Map DOM elements to their data
const elementDataMap = new Map();

const button1 = document.querySelector("#btn1");
const button2 = document.querySelector("#btn2");

elementDataMap.set(button1, { clickCount: 0 });
elementDataMap.set(button2, { clickCount: 0 });

button1.addEventListener("click", () =&gt; {
  const data = elementDataMap.get(button1);
  data.clickCount++;
  console.log(`Clicked ${data.clickCount} times`);
});
</code></pre>
<p><strong>Use Set When:</strong></p>
<p><strong>1. Removing Duplicates</strong></p>
<pre><code class="language-javascript">// Get unique users from multiple lists
const group1Users = ["Raj", "Priya", "Anil"];
const group2Users = ["Priya", "Anil", "Vikram"];

const allUsers = new Set([...group1Users, ...group2Users]);
console.log(allUsers); // Set(4) { 'Raj', 'Priya', 'Anil', 'Vikram' }
</code></pre>
<p><strong>2. Checking Membership Quickly</strong></p>
<pre><code class="language-javascript">// Check if user has permission
const adminUsers = new Set(["user1", "user5", "user8"]);

function isAdmin(userId) {
  return adminUsers.has(userId); // O(1) lookup
}

console.log(isAdmin("user5")); // true
console.log(isAdmin("user3")); // false
</code></pre>
<p><strong>3. Finding Unique Elements</strong></p>
<pre><code class="language-javascript">// Find unique words in a text
const text = "javascript javascript is fun is fun";
const words = text.split(" ");
const uniqueWords = new Set(words);

console.log(uniqueWords); // Set(4) { 'javascript', 'is', 'fun' }
console.log(`Unique words: ${uniqueWords.size}`);
</code></pre>
<p><strong>4. Tracking Visited/Processed Items</strong></p>
<pre><code class="language-javascript">// Web crawler tracking visited URLs
const visitedUrls = new Set();

async function crawl(url) {
  if (visitedUrls.has(url)) {
    return; // Already visited
  }
  
  visitedUrls.add(url);
  console.log(`Crawling ${url}`);
  // Fetch and process...
}
</code></pre>
<p><strong>5. Creating Filtered Lists</strong></p>
<pre><code class="language-javascript">// Get common elements between two arrays
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];

const set1 = new Set(arr1);
const common = arr2.filter(item =&gt; set1.has(item));

console.log(common); // [3, 4, 5]
</code></pre>
<hr />
<h2><strong>6. Real-World Examples</strong></h2>
<p><strong>Example 1: User Authentication and Session Management (Map)</strong></p>
<pre><code class="language-javascript">// Store user sessions
const activeSessions = new Map();

class SessionManager {
  createSession(userId, sessionToken) {
    const sessionData = {
      userId: userId,
      createdAt: new Date(),
      lastActivity: new Date()
    };
    activeSessions.set(sessionToken, sessionData);
    console.log(`Session created. Active sessions: ${activeSessions.size}`);
  }
  
  getSession(sessionToken) {
    return activeSessions.get(sessionToken);
  }
  
  isValidSession(sessionToken) {
    if (!activeSessions.has(sessionToken)) {
      return false;
    }
    
    const session = activeSessions.get(sessionToken);
    const now = new Date();
    const expireTime = 30 * 60 * 1000; // 30 minutes
    
    if (now - session.lastActivity &gt; expireTime) {
      activeSessions.delete(sessionToken);
      return false;
    }
    
    session.lastActivity = now;
    return true;
  }
  
  endSession(sessionToken) {
    activeSessions.delete(sessionToken);
  }
  
  getActiveSessions() {
    return activeSessions.size;
  }
}

// Usage
const manager = new SessionManager();
manager.createSession("user1", "token_abc123");
manager.createSession("user2", "token_xyz789");
console.log("Active:", manager.getActiveSessions()); // 2
manager.endSession("token_abc123");
console.log("Active:", manager.getActiveSessions()); // 1
</code></pre>
<p><strong>Example 2: Tracking Unique Visitors (Set)</strong></p>
<pre><code class="language-javascript">// Track unique visitors to a website
const uniqueVisitors = new Set();
const visitorLog = [];

function recordVisit(userId) {
  // Add to unique visitors
  if (!uniqueVisitors.has(userId)) {
    uniqueVisitors.add(userId);
    console.log(`New visitor: ${userId}`);
  } else {
    console.log(`Returning visitor: ${userId}`);
  }
  
  // Log the visit
  visitorLog.push({
    userId: userId,
    timestamp: new Date()
  });
}

function getUniqueVisitorCount() {
  return uniqueVisitors.size;
}

// Usage
recordVisit("user1");
recordVisit("user2");
recordVisit("user1"); // Returning visitor
recordVisit("user3");
console.log(`Total unique visitors: ${getUniqueVisitorCount()}`); // 3
</code></pre>
<p><strong>Example 3: Product Inventory with Tags (Map + Set)</strong></p>
<pre><code class="language-javascript">// Store products with their tags (Set for unique tags)
const productInventory = new Map();

class Product {
  constructor(id, name, price) {
    this.id = id;
    this.name = name;
    this.price = price;
    this.tags = new Set();
  }
  
  addTag(tag) {
    this.tags.add(tag);
  }
  
  hasTag(tag) {
    return this.tags.has(tag);
  }
  
  getTags() {
    return Array.from(this.tags);
  }
}

// Add products
const laptop = new Product(1, "Laptop", 50000);
laptop.addTag("electronics");
laptop.addTag("computers");
laptop.addTag("expensive");

productInventory.set(1, laptop);

// Query products
function findProductsByTag(tag) {
  const results = [];
  for (const [id, product] of productInventory) {
    if (product.hasTag(tag)) {
      results.push(product);
    }
  }
  return results;
}

console.log(findProductsByTag("electronics")); // [Product...]
console.log(laptop.getTags()); // ["electronics", "computers", "expensive"]
</code></pre>
<p><strong>Example 4: Finding Common and Unique Elements (Set)</strong></p>
<pre><code class="language-javascript">// Comparing two lists
const team1 = new Set(["Raj", "Priya", "Anil", "Sneha"]);
const team2 = new Set(["Priya", "Anil", "Vikram", "Arjun"]);

// Common members
const commonMembers = new Set([...team1].filter(member =&gt; team2.has(member)));
console.log("Common:", commonMembers); // Set(2) { 'Priya', 'Anil' }

// Only in team1
const onlyTeam1 = new Set([...team1].filter(member =&gt; !team2.has(member)));
console.log("Only team1:", onlyTeam1); // Set(2) { 'Raj', 'Sneha' }

// Only in team2
const onlyTeam2 = new Set([...team2].filter(member =&gt; !team1.has(member)));
console.log("Only team2:", onlyTeam2); // Set(2) { 'Vikram', 'Arjun' }

// All members combined (union)
const allMembers = new Set([...team1, ...team2]);
console.log("All:", allMembers); // Set(6) { 'Raj', 'Priya', ... }
</code></pre>
<hr />
<h2><strong>7. Map and Set Methods Reference</strong></h2>
<p><strong>Map Methods:</strong></p>
<pre><code class="language-javascript">const map = new Map();

// Add/Update
map.set(key, value);

// Retrieve
map.get(key);

// Check existence
map.has(key);

// Delete
map.delete(key);

// Clear all
map.clear();

// Size
map.size;

// Iterate
for (const [key, value] of map) { }
map.forEach((value, key) =&gt; { });
map.keys();
map.values();
map.entries();
</code></pre>
<p><strong>Set Methods:</strong></p>
<pre><code class="language-javascript">const set = new Set();

// Add
set.add(value);

// Check existence
set.has(value);

// Delete
set.delete(value);

// Clear all
set.clear();

// Size
set.size;

// Iterate
for (const value of set) { }
set.forEach((value) =&gt; { });
set.values();
set.keys(); // Same as values()
set.entries(); // Returns [value, value] pairs
</code></pre>
<hr />
<h2><strong>8. Performance Comparison</strong></h2>
<pre><code class="language-javascript">// Check existence performance comparison

// With Array - O(n) complexity
const arr = Array.from({ length: 100000 }, (_, i) =&gt; i);
console.time("Array.includes");
arr.includes(99999); // Slow - must check each item
console.timeEnd("Array.includes"); // ~0.5ms+

// With Set - O(1) complexity
const set = new Set(arr);
console.time("Set.has");
set.has(99999); // Fast - instant lookup
console.timeEnd("Set.has"); // ~0.001ms

// For large datasets, Set is much faster for membership checks
</code></pre>
<hr />
<h2><strong>9. Converting Between Structures</strong></h2>
<pre><code class="language-javascript">// Array to Set (remove duplicates)
const arr = [1, 2, 2, 3, 3, 3];
const set = new Set(arr);
console.log(set); // Set(3) { 1, 2, 3 }

// Set to Array
const set2 = new Set(["a", "b", "c"]);
const arr2 = Array.from(set2);
console.log(arr2); // ["a", "b", "c"]

// Or using spread
const arr3 = [...set2];
console.log(arr3); // ["a", "b", "c"]

// Object to Map
const obj = { name: "Raj", age: 25 };
const map = new Map(Object.entries(obj));
console.log(map.get("name")); // "Raj"

// Map to Object
const map2 = new Map([["x", 1], ["y", 2]]);
const obj2 = Object.fromEntries(map2);
console.log(obj2); // { x: 1, y: 2 }
</code></pre>
<hr />
<h2><strong>10. Common Patterns</strong></h2>
<p><strong>Pattern 1: Group By Using Map</strong></p>
<pre><code class="language-javascript">const students = [
  { name: "Raj", grade: "A" },
  { name: "Priya", grade: "B" },
  { name: "Anil", grade: "A" },
  { name: "Sneha", grade: "C" }
];

// Group students by grade
const studentsByGrade = new Map();

students.forEach(student =&gt; {
  if (!studentsByGrade.has(student.grade)) {
    studentsByGrade.set(student.grade, []);
  }
  studentsByGrade.get(student.grade).push(student);
});

console.log(studentsByGrade.get("A")); // [Raj, Anil]
console.log(studentsByGrade.get("B")); // [Priya]
</code></pre>
<p><strong>Pattern 2: Counter Using Map</strong></p>
<pre><code class="language-javascript">const text = "hello world hello javascript";
const words = text.split(" ");

const wordCount = new Map();

words.forEach(word =&gt; {
  wordCount.set(word, (wordCount.get(word) || 0) + 1);
});

console.log(wordCount.get("hello")); // 2
console.log(wordCount.get("world")); // 1
</code></pre>
<p><strong>Pattern 3: Cache Using Map</strong></p>
<pre><code class="language-javascript">const cache = new Map();

function expensiveCalculation(n) {
  if (cache.has(n)) {
    console.log(`Returning cached result for ${n}`);
    return cache.get(n);
  }
  
  console.log(`Calculating result for ${n}`);
  const result = n * n * n; // Expensive calculation
  cache.set(n, result);
  return result;
}

console.log(expensiveCalculation(5)); // Calculating...
console.log(expensiveCalculation(5)); // Cached
console.log(expensiveCalculation(10)); // Calculating...
</code></pre>
<hr />
<h2><strong>Conclusion</strong></h2>
<p>Map and Set are powerful data structures that solve specific problems better than objects and arrays:</p>
<p><strong>Use Map When:</strong></p>
<ul>
<li><p>Keys might not be strings</p>
</li>
<li><p>You need frequent add/delete operations</p>
</li>
<li><p>You need a built-in size property</p>
</li>
<li><p>You want to use objects as keys</p>
</li>
</ul>
<p><strong>Use Set When:</strong></p>
<ul>
<li><p>You need unique values only</p>
</li>
<li><p>You need fast membership checking</p>
</li>
<li><p>You want to eliminate duplicates</p>
</li>
<li><p>You're tracking visited/processed items</p>
</li>
</ul>
<p><strong>Key Takeaways:</strong></p>
<ol>
<li><p>Map stores key-value pairs with any key type</p>
</li>
<li><p>Set stores unique values of any type</p>
</li>
<li><p>Map has better performance for frequent updates</p>
</li>
<li><p>Set has better performance for membership checks</p>
</li>
<li><p>Both are more efficient than objects and arrays for their respective use cases</p>
</li>
</ol>
<p>Master Map and Set, and you'll write more efficient, cleaner code. They're fundamental tools in modern JavaScript that every developer should be comfortable using.</p>
]]></content:encoded></item></channel></rss>