<?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[Nureddin Badawi]]></title><description><![CDATA[I build scalable, high-performance applications across frontend, backend, and cloud infrastructure. and I excel in modern React-based frontend ecosystems.]]></description><link>https://nurdin.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 11:14:23 GMT</lastBuildDate><atom:link href="https://nurdin.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building Robust Node.js Microservices: Best Practices for Modern Architectures]]></title><description><![CDATA[Node.js has become a go-to choice for building microservices due to its performance, scalability for I/O-bound operations, and the unified JavaScript ecosystem. However, transitioning from monolithic applications to a distributed microservices archit...]]></description><link>https://nurdin.dev/building-robust-nodejs-microservices-best-practices-for-modern-architectures</link><guid isPermaLink="true">https://nurdin.dev/building-robust-nodejs-microservices-best-practices-for-modern-architectures</guid><category><![CDATA[Microservices]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Thu, 22 May 2025 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748040779763/fc7a6d4c-95aa-4911-8032-05154ad925ad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Node.js has become a go-to choice for building microservices due to its performance, scalability for I/O-bound operations, and the unified JavaScript ecosystem. However, transitioning from monolithic applications to a distributed microservices architecture introduces new challenges. Adhering to best practices is crucial for building maintainable, resilient, and scalable Node.js microservices.</p>
<h2 id="heading-1-embrace-the-single-responsibility-principle-srp">1. Embrace the Single Responsibility Principle (SRP)</h2>
<p>Each microservice should do one thing and do it well. Avoid creating "mini-monoliths."</p>
<ul>
<li><p><strong>Why:</strong> Simplifies development, testing, deployment, and scaling of individual services. Changes in one service are less likely to impact others.</p>
</li>
<li><p><strong>How:</strong> Clearly define the bounded context for each service. For example, an e-commerce application might have separate services for <code>users</code>, <code>products</code>, <code>orders</code>, and <code>payments</code>.</p>
</li>
</ul>
<h2 id="heading-2-design-for-statelessness">2. Design for Statelessness</h2>
<p>Whenever possible, your Node.js microservices should be stateless.</p>
<ul>
<li><p><strong>Why:</strong> Stateless services are easier to scale horizontally, replace, and load balance. State can be offloaded to dedicated state stores (databases, caches).</p>
</li>
<li><p><strong>How:</strong> Avoid storing session data or any request-specific state in the service's memory. If state is needed, retrieve it from an external store (e.g., Redis, PostgreSQL, MongoDB) on each request or use techniques like JWTs for session information.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Bad: Storing session in memory</span>
 <span class="hljs-keyword">const</span> sessions = {};
 app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
   <span class="hljs-keyword">const</span> sessionId = generateSessionId();
   sessions[sessionId] = { <span class="hljs-attr">userId</span>: req.body.userId };
   res.cookie(<span class="hljs-string">'sessionId'</span>, sessionId).send(<span class="hljs-string">'Logged in'</span>);
 });

<span class="hljs-comment">// Good: Using JWT (stateless)</span>
 app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
   <span class="hljs-keyword">const</span> user = authenticateUser(req.body.username, req.body.password);
   <span class="hljs-keyword">if</span> (user) {
     <span class="hljs-keyword">const</span> token = jwt.sign({ <span class="hljs-attr">userId</span>: user.id }, process.env.JWT_SECRET, { <span class="hljs-attr">expiresIn</span>: <span class="hljs-string">'1h'</span> });
     res.json({ token });
   } <span class="hljs-keyword">else</span> {
     res.status(<span class="hljs-number">401</span>).send(<span class="hljs-string">'Authentication failed'</span>);
   }
 });
</code></pre>
<h2 id="heading-3-centralized-configuration-management">3. Centralized Configuration Management</h2>
<p>Externalize your configuration. Don't hardcode connection strings, API keys, or environment-specific settings.</p>
<ul>
<li><p><strong>Why:</strong> Allows for different configurations across environments (dev, staging, prod) without code changes. Enhances security by keeping secrets out of the codebase.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p>Use environment variables (e.g., <code>process.env.DATABASE_URL</code>).</p>
</li>
<li><p>Employ configuration management tools like HashiCorp Consul, AWS Parameter Store, or Azure App Configuration.</p>
</li>
<li><p>Use <code>.env</code> files for local development (e.g., with the <code>dotenv</code> package).</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// config.js</span>
<span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;
dotenv.config(); <span class="hljs-comment">// Loads .env file into process.env</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  <span class="hljs-attr">port</span>: process.env.PORT || <span class="hljs-number">3000</span>,
  <span class="hljs-attr">databaseUrl</span>: process.env.DATABASE_URL,
  <span class="hljs-attr">apiKey</span>: process.env.API_KEY,
  <span class="hljs-comment">// ... other configurations</span>
};
</code></pre>
<h2 id="heading-4-implement-comprehensive-logging-and-monitoring">4. Implement Comprehensive Logging and Monitoring</h2>
<p>In a distributed system, understanding what's happening is critical.</p>
<ul>
<li><p><strong>Why:</strong> Essential for debugging, tracing requests across services, and identifying performance bottlenecks.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p><strong>Structured Logging:</strong> Use JSON or another machine-readable format. Libraries like <code>pino</code> or <code>winston</code> are excellent. Include correlation IDs to trace requests across multiple services.</p>
</li>
<li><p><strong>Centralized Logging:</strong> Ship logs to a central logging platform (e.g., ELK Stack, Splunk, Datadog).</p>
</li>
<li><p><strong>Monitoring &amp; Alerting:</strong> Track key metrics (CPU, memory, latency, error rates) using tools like Prometheus/Grafana, Datadog, or New Relic. Set up alerts for critical issues.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Using pino for structured logging</span>
<span class="hljs-keyword">import</span> pino <span class="hljs-keyword">from</span> <span class="hljs-string">"pino"</span>;
<span class="hljs-keyword">const</span> logger = pino({
  <span class="hljs-attr">level</span>: process.env.LOG_LEVEL || <span class="hljs-string">"info"</span>,
  <span class="hljs-comment">// Add a correlationId hook if using a request context</span>
});

<span class="hljs-comment">// logger.info({ orderId: '123', userId: 'abc' }, 'Order processed');</span>
</code></pre>
<h2 id="heading-5-expose-health-check-endpoints">5. Expose Health Check Endpoints</h2>
<p>Each microservice should expose a health check endpoint.</p>
<ul>
<li><p><strong>Why:</strong> Orchestration tools (like Kubernetes) and load balancers use these to determine if a service instance is healthy and ready to receive traffic.</p>
</li>
<li><p><strong>How:</strong> Create an HTTP endpoint (e.g., <code>/healthz</code> or <code>/status</code>) that checks the status of critical dependencies (database connections, external service availability) and returns a success (e.g., 200 OK) or failure status.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Example health check in an Express app</span>
app.get(<span class="hljs-string">"/healthz"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-comment">// Perform checks (e.g., database connectivity)</span>
  <span class="hljs-keyword">const</span> isHealthy = checkDatabaseConnection() &amp;&amp; checkExternalService();
  <span class="hljs-keyword">if</span> (isHealthy) {
    res.status(<span class="hljs-number">200</span>).send(<span class="hljs-string">"OK"</span>);
  } <span class="hljs-keyword">else</span> {
    res.status(<span class="hljs-number">503</span>).send(<span class="hljs-string">"Service Unavailable"</span>); <span class="hljs-comment">// 503 Service Unavailable</span>
  }
});
</code></pre>
<h2 id="heading-6-utilize-asynchronous-communication">6. Utilize Asynchronous Communication</h2>
<p>For non-blocking operations and inter-service communication where an immediate response isn't required, use asynchronous patterns.</p>
<ul>
<li><p><strong>Why:</strong> Improves resilience (if a service is temporarily down, messages can be queued) and decoupling. Prevents cascading failures.</p>
</li>
<li><p><strong>How:</strong> Use message brokers like RabbitMQ, Kafka, AWS SQS, or Google Pub/Sub. Node.js excels at handling these asynchronous workflows.</p>
</li>
</ul>
<h2 id="heading-7-design-consistent-and-versioned-apis">7. Design Consistent and Versioned APIs</h2>
<p>Whether you choose REST, GraphQL, or gRPC, ensure your APIs are well-defined, consistent, and versioned.</p>
<ul>
<li><p><strong>Why:</strong> Makes services easier to consume and evolve independently.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p><strong>REST:</strong> Use standard HTTP methods, status codes, and clear resource naming. Version APIs (e.g., <code>/v1/users</code>).</p>
</li>
<li><p><strong>GraphQL:</strong> Consider GraphQL Federation if you have many services contributing to a unified data graph (as discussed previously).</p>
</li>
<li><p><strong>gRPC:</strong> For high-performance internal communication, especially if you have polyglot services.</p>
</li>
<li><p>Use tools like OpenAPI (Swagger) for REST API documentation and contract definition.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-8-implement-robust-error-handling">8. Implement Robust Error Handling</h2>
<p>Gracefully handle errors and provide meaningful error responses.</p>
<ul>
<li><p><strong>Why:</strong> Prevents service crashes and helps consumers understand issues.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p>Use <code>try...catch</code> for synchronous code and <code>.catch()</code> for Promises.</p>
</li>
<li><p>Implement global error handlers in your framework (e.g., Express error-handling middleware).</p>
</li>
<li><p>Return appropriate HTTP status codes and error messages.</p>
</li>
<li><p>Avoid leaking sensitive stack traces in production.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-9-prioritize-security">9. Prioritize Security</h2>
<p>Security is paramount, especially in a distributed environment.</p>
<ul>
<li><p><strong>Why:</strong> More services mean more potential attack surfaces.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p><strong>Authentication &amp; Authorization:</strong> Secure service-to-service communication (e.g., OAuth 2.0, JWTs, mTLS).</p>
</li>
<li><p><strong>Input Validation:</strong> Validate all incoming data (payloads, query params, headers). Libraries like <code>joi</code> or <code>zod</code> are helpful.</p>
</li>
<li><p><strong>Rate Limiting &amp; Throttling:</strong> Protect services from abuse.</p>
</li>
<li><p><strong>Secrets Management:</strong> Use tools like HashiCorp Vault or cloud provider KMS.</p>
</li>
<li><p><strong>Regular Dependency Scans:</strong> Use <code>npm audit</code> or tools like Snyk to find and fix vulnerabilities in dependencies.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-10-containerize-your-services-docker">10. Containerize Your Services (Docker)</h2>
<p>Package your Node.js microservices into Docker containers.</p>
<ul>
<li><p><strong>Why:</strong> Ensures consistency across environments, simplifies deployment, and works well with orchestration platforms like Kubernetes.</p>
</li>
<li><p><strong>How:</strong> Write efficient <code>Dockerfile</code>s. Use multi-stage builds to keep image sizes small.</p>
</li>
</ul>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Dockerfile example (simplified)</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine AS builder
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /usr/src/app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci --only=production</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-comment"># RUN npm run build # If you have a build step</span>

<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /usr/src/app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /usr/src/app/node_modules ./node_modules</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /usr/src/app/dist ./dist <span class="hljs-comment"># Or copy source if no build</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /usr/src/app/package.json ./</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">3000</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [ <span class="hljs-string">"node"</span>, <span class="hljs-string">"dist/server.js"</span> ] <span class="hljs-comment"># Or your entry point</span></span>
</code></pre>
<h2 id="heading-11-comprehensive-testing-strategy">11. Comprehensive Testing Strategy</h2>
<ul>
<li><p><strong>Why:</strong> Ensures reliability and allows for confident refactoring and deployments.</p>
</li>
<li><p><strong>How:</strong></p>
<ul>
<li><p><strong>Unit Tests:</strong> Test individual modules/functions in isolation (e.g., using Jest, Mocha).</p>
</li>
<li><p><strong>Integration Tests:</strong> Test interactions between your service and its direct dependencies (e.g., database, external APIs).</p>
</li>
<li><p><strong>Contract Tests:</strong> Verify that services adhere to their API contracts (e.g., using Pact). This is crucial for inter-service communication.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-12-choose-lightweight-frameworks">12. Choose Lightweight Frameworks</h2>
<p>While Express.js is very popular and flexible, consider even more lightweight frameworks if your microservice is very focused.</p>
<ul>
<li><p><strong>Why:</strong> Smaller footprint, faster startup times.</p>
</li>
<li><p><strong>How:</strong> Frameworks like Fastify are known for high performance and low overhead. For very simple services, you might even use the native <code>http</code> module, though a minimal framework often provides useful abstractions.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building Node.js microservices effectively requires a shift in mindset from monolithic development. By focusing on these best practices, you can create a distributed system that is scalable, resilient, maintainable, and leverages the strengths of Node.js. Remember that these are guidelines; adapt them to your specific project needs and team capabilities.</p>
]]></content:encoded></item><item><title><![CDATA[Streamline Your Data Wrangling: Introducing Object.groupBy() and Map.groupBy()]]></title><description><![CDATA[As JavaScript developers, we often find ourselves needing to group elements within an array based on a specific criterion. Before ECMAScript 2024, this typically involved a manual reduce operation or a loop with some conditional logic, which could so...]]></description><link>https://nurdin.dev/streamline-your-data-wrangling-introducing-objectgroupby-and-mapgroupby</link><guid isPermaLink="true">https://nurdin.dev/streamline-your-data-wrangling-introducing-objectgroupby-and-mapgroupby</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Sat, 17 May 2025 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748037402664/ef316fe3-d46e-498d-91b0-be0a73865d85.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As JavaScript developers, we often find ourselves needing to group elements within an array based on a specific criterion. Before ECMAScript 2024, this typically involved a manual <code>reduce</code> operation or a loop with some conditional logic, which could sometimes be a bit verbose.</p>
<p>Enter <code>Object.groupBy()</code> and <code>Map.groupBy()</code> – two new static methods that provide a more declarative and concise way to achieve this common task. [8, 11] They take an iterable (like an array) and a callback function. The callback function determines the key under which each element should be grouped.</p>
<h2 id="heading-the-power-of-grouping-simplified">The Power of Grouping, Simplified</h2>
<p>Imagine you have an array of product objects, and you want to group them by their category.</p>
<h3 id="heading-before-objectgroupby">Before <code>Object.groupBy()</code>:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> products = [
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Laptop"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">1200</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Shirt"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">50</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Keyboard"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">75</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Jeans"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">80</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Mouse"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">25</span> },
];

<span class="hljs-comment">// Using reduce (a common pre-ES2024 approach)</span>
<span class="hljs-keyword">const</span> productsByCategoryOld = products.reduce(<span class="hljs-function">(<span class="hljs-params">acc, product</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> key = product.category;
  <span class="hljs-keyword">if</span> (!acc[key]) {
    acc[key] = [];
  }
  acc[key].push(product);
  <span class="hljs-keyword">return</span> acc;
}, {});

<span class="hljs-built_in">console</span>.log(productsByCategoryOld);
<span class="hljs-comment">/*
Output:
{
  Electronics: [
    { name: 'Laptop', category: 'Electronics', price: 1200 },
    { name: 'Keyboard', category: 'Electronics', price: 75 },
    { name: 'Mouse', category: 'Electronics', price: 25 }
  ],
  Apparel: [
    { name: 'Shirt', category: 'Apparel', price: 50 },
    { name: 'Jeans', category: 'Apparel', price: 80 }
  ]
}
*/</span>
</code></pre>
<p>This works, but it requires a bit of boilerplate to initialize the arrays for each group.</p>
<h3 id="heading-with-objectgroupby">With <code>Object.groupBy()</code>:</h3>
<p>Now, let's see how <code>Object.groupBy()</code> simplifies this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> products = [
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Laptop"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">1200</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Shirt"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">50</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Keyboard"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">75</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Jeans"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">80</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Mouse"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">25</span> },
];

<span class="hljs-keyword">const</span> productsByCategoryNew = <span class="hljs-built_in">Object</span>.groupBy(products, <span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> product.category);

<span class="hljs-built_in">console</span>.log(productsByCategoryNew);
<span class="hljs-comment">/*
Output (note: the prototype of the resulting object is null):
{
  Electronics: [
    { name: 'Laptop', category: 'Electronics', price: 1200 },
    { name: 'Keyboard', category: 'Electronics', price: 75 },
    { name: 'Mouse', category: 'Electronics', price: 25 }
  ],
  Apparel: [
    { name: 'Shirt', category: 'Apparel', price: 50 },
    { name: 'Jeans', category: 'Apparel', price: 80 }
  ]
}
*/</span>
</code></pre>
<p>Much cleaner, right? The <code>Object.groupBy()</code> method returns a null-prototype object where keys are the group identifiers (in this case, "Electronics" and "Apparel") and values are arrays of the elements belonging to that group. [11]</p>
<h3 id="heading-when-to-use-mapgroupby">When to Use <code>Map.groupBy()</code>?</h3>
<p>The <code>Map.groupBy()</code> method is very similar but returns a <code>Map</code> instance instead of a plain object. [11] This can be advantageous when:</p>
<ul>
<li><p><strong>You need to use non-string keys:</strong> Map keys can be any value (objects, functions, etc.), whereas plain object keys are always coerced to strings (or Symbols).</p>
</li>
<li><p><strong>You need to preserve insertion order (for keys):</strong> Maps remember the original insertion order of keys.</p>
</li>
<li><p><strong>You need built-in size property and other Map methods:</strong> <code>Map</code> objects have a handy <code>.size</code> property and methods like <code>.has()</code>, <code>.get()</code>, <code>.set()</code>, <code>.delete()</code>, etc.</p>
</li>
</ul>
<p>Here's the same example using <code>Map.groupBy()</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> products = [
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Laptop"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">1200</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Shirt"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">50</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Keyboard"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">75</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Jeans"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Apparel"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">80</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">"Mouse"</span>, <span class="hljs-attr">category</span>: <span class="hljs-string">"Electronics"</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">25</span> },
];

<span class="hljs-keyword">const</span> productsByCategoryMap = <span class="hljs-built_in">Map</span>.groupBy(products, <span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> product.category);

<span class="hljs-built_in">console</span>.log(productsByCategoryMap);
<span class="hljs-comment">/*
Output:
Map(2) {
  'Electronics' =&gt; [
    { name: 'Laptop', category: 'Electronics', price: 1200 },
    { name: 'Keyboard', category: 'Electronics', price: 75 },
    { name: 'Mouse', category: 'Electronics', price: 25 }
  ],
  'Apparel' =&gt; [
    { name: 'Shirt', category: 'Apparel', price: 50 },
    { name: 'Jeans', category: 'Apparel', price: 80 }
  ]
}
*/</span>

<span class="hljs-comment">// You can then easily iterate or access elements:</span>
<span class="hljs-built_in">console</span>.log(productsByCategoryMap.get(<span class="hljs-string">"Electronics"</span>));
</code></pre>
<h2 id="heading-why-is-this-a-very-useful-tweak">Why is this a "Very Useful Tweak"?</h2>
<ol>
<li><p><strong>Readability:</strong> The intent of the code becomes much clearer. <code>Object.groupBy(items, callback)</code> immediately tells you what's happening.</p>
</li>
<li><p><strong>Conciseness:</strong> It significantly reduces the amount of boilerplate code needed for a common operation.</p>
</li>
<li><p><strong>Reduced Errors:</strong> Less manual logic means fewer opportunities for small mistakes in setting up the accumulator or managing arrays.</p>
</li>
<li><p><strong>Standardization:</strong> It provides a standard, built-in way to perform grouping, leading to more consistent codebases. [8]</p>
</li>
</ol>
<h2 id="heading-browser-support-and-transpilation">Browser Support and Transpilation</h2>
<p>As <code>Object.groupBy()</code> and <code>Map.groupBy()</code> are part of ECMAScript 2024, modern browsers are quickly adopting them. [1, 8] For older environments, you'll likely need to rely on transpilers like Babel along with polyfills to use this feature. [5] However, as you build projects for 2025 and beyond, you can expect to use these natively more and more.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>While seemingly small, <code>Object.groupBy()</code> and <code>Map.groupBy()</code> are excellent examples of how JavaScript continues to evolve to make developers' lives easier. [1, 8] They address a frequent need with an elegant and efficient solution, allowing you to write cleaner, more maintainable code. So, next time you need to group data, remember these handy additions!</p>
]]></content:encoded></item><item><title><![CDATA[Immutable Array Updates Made Easy: Meet Array.prototype.with()]]></title><description><![CDATA[As JavaScript developers, especially when working with state management in frameworks like React, Vue, or Angular, or simply aiming for more predictable code, immutability is a key concept. Modifying an array "in place" can lead to unexpected side ef...]]></description><link>https://nurdin.dev/immutable-array-updates-made-easy-meet-arrayprototypewith</link><guid isPermaLink="true">https://nurdin.dev/immutable-array-updates-made-easy-meet-arrayprototypewith</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Fri, 09 May 2025 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748037782863/ba17cecd-7c8d-48e3-8e83-1192417e3749.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As JavaScript developers, especially when working with state management in frameworks like React, Vue, or Angular, or simply aiming for more predictable code, immutability is a key concept. Modifying an array "in place" can lead to unexpected side effects. The traditional way to update an element in an array immutably often involved methods like <code>slice()</code>, <code>map()</code>, or the spread operator, which could sometimes feel a bit clunky for a simple targeted update.</p>
<p>Enter <code>Array.prototype.with(index, value)</code>, a new method introduced in ECMAScript 2023 (ES14). It provides a clean, direct, and immutable way to get a <em>new</em> array with an element at a specific index replaced with a new value. [2, 6]</p>
<h2 id="heading-the-old-way-vs-the-new-way">The Old Way vs. The New Way</h2>
<p>Let's say you have an array of tasks, and you want to update the status of a specific task without mutating the original array.</p>
<h3 id="heading-before-arrayprototypewith">Before <code>Array.prototype.with()</code>:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> tasks = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Learn JavaScript"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"done"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Write an article"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"in-progress"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Deploy to production"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"todo"</span> },
];

<span class="hljs-keyword">const</span> taskIndexToUpdate = <span class="hljs-number">1</span>;
<span class="hljs-keyword">const</span> newStatus = <span class="hljs-string">"done"</span>;

<span class="hljs-comment">// Common approach using map:</span>
<span class="hljs-keyword">const</span> updatedTasksMap = tasks.map(<span class="hljs-function">(<span class="hljs-params">task, index</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (index === taskIndexToUpdate) {
    <span class="hljs-keyword">return</span> { ...task, <span class="hljs-attr">status</span>: newStatus };
  }
  <span class="hljs-keyword">return</span> task;
});

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Original tasks (map):"</span>, tasks);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Updated tasks (map):"</span>, updatedTasksMap);
<span class="hljs-comment">/*
Original tasks (map): [
  { id: 1, text: 'Learn JavaScript', status: 'done' },
  { id: 2, text: 'Write an article', status: 'in-progress' },
  { id: 3, text: 'Deploy to production', status: 'todo' }
]
Updated tasks (map): [
  { id: 1, text: 'Learn JavaScript', status: 'done' },
  { id: 2, text: 'Write an article', status: 'done' }, // Updated
  { id: 3, text: 'Deploy to production', status: 'todo' }
]
*/</span>

<span class="hljs-comment">// Another approach using slice and spread (more verbose for single element):</span>
<span class="hljs-keyword">const</span> updatedTasksSlice = [
  ...tasks.slice(<span class="hljs-number">0</span>, taskIndexToUpdate),
  { ...tasks[taskIndexToUpdate], <span class="hljs-attr">status</span>: newStatus },
  ...tasks.slice(taskIndexToUpdate + <span class="hljs-number">1</span>),
];
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Updated tasks (slice):"</span>, updatedTasksSlice);
</code></pre>
<p>While these methods work and preserve immutability, they require a bit more boilerplate, especially the <code>slice</code> method for a single update. The <code>map</code> approach iterates over the entire array, which is fine for many cases but less direct if you know the index.</p>
<h3 id="heading-with-arrayprototypewith">With <code>Array.prototype.with()</code>:</h3>
<p>Now, let's see how <code>with()</code> simplifies this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> tasks = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Learn JavaScript"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"done"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Write an article"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"in-progress"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Deploy to production"</span>, <span class="hljs-attr">status</span>: <span class="hljs-string">"todo"</span> },
];

<span class="hljs-keyword">const</span> taskIndexToUpdate = <span class="hljs-number">1</span>;
<span class="hljs-keyword">const</span> newStatus = <span class="hljs-string">"done"</span>;

<span class="hljs-comment">// Using .with()</span>
<span class="hljs-keyword">const</span> updatedTasksWith = tasks.with(taskIndexToUpdate, {
  ...tasks[taskIndexToUpdate], <span class="hljs-comment">// Spread the existing task to only update status</span>
  <span class="hljs-attr">status</span>: newStatus,
});

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Original tasks (with):"</span>, tasks); <span class="hljs-comment">// Unchanged!</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Updated tasks (with):"</span>, updatedTasksWith);
<span class="hljs-comment">/*
Original tasks (with): [
  { id: 1, text: 'Learn JavaScript', status: 'done' },
  { id: 2, text: 'Write an article', status: 'in-progress' },
  { id: 3, text: 'Deploy to production', status: 'todo' }
]
Updated tasks (with): [
  { id: 1, text: 'Learn JavaScript', status: 'done' },
  { id: 2, text: 'Write an article', status: 'done' }, // Updated
  { id: 3, text: 'Deploy to production', status: 'todo' }
]
*/</span>
</code></pre>
<p>Notice how <code>tasks.with(index, newValue)</code> clearly expresses the intent: "give me a new array based on <code>tasks</code>, but with the element at <code>index</code> changed to <code>newValue</code>." The original <code>tasks</code> array remains untouched. [2, 6]</p>
<h2 id="heading-why-is-arrayprototypewith-a-very-useful-tweak">Why is <code>Array.prototype.with()</code> a "Very Useful Tweak"?</h2>
<ol>
<li><p><strong>Guaranteed Immutability:</strong> Its primary purpose is to return a <em>new</em> array, reinforcing immutable patterns. This helps prevent bugs related to shared mutable state. [2]</p>
</li>
<li><p><strong>Readability &amp; Conciseness:</strong> The code <code>arr.with(index, value)</code> is very declarative and easy to understand at a glance compared to manual slicing or mapping for a single element replacement.</p>
</li>
<li><p><strong>Reduced Cognitive Load:</strong> You don't have to mentally parse more complex constructions like <code>slice/concat</code> or a <code>map</code> function just to change one item.</p>
</li>
<li><p><strong>Method Chaining:</strong> Like other modern array methods that return new arrays (e.g., <code>map</code>, <code>filter</code>), <code>with()</code> can be easily chained.</p>
</li>
<li><p><strong>Consistency:</strong> It joins other new immutable array methods like <code>toSorted()</code>, <code>toReversed()</code>, and <code>toSpliced()</code>, providing a more consistent API for immutable operations. [6]</p>
</li>
</ol>
<h2 id="heading-important-considerations">Important Considerations</h2>
<ul>
<li><p><strong>Shallow Copy for the New Value:</strong> If the <code>newValue</code> you provide to <code>with()</code> is an object or array, <code>with()</code> itself doesn't deep clone it. If you're updating an object within the array (like in our <code>tasks</code> example), you still need to ensure you're providing a <em>new</em> object for that element (e.g., using spread syntax <code>{ ...oldObject, propertyToChange: newValue }</code>) if you want to avoid mutating the original nested object. The <code>with()</code> method handles the immutability of the array itself.</p>
</li>
<li><p><strong>Index Out of Bounds:</strong> If the <code>index</code> is out of bounds, <code>with()</code> will throw a <code>RangeError</code>, similar to how trying to assign to an out-of-bounds index with strict mode might behave, but more explicit. [2]</p>
</li>
</ul>
<h2 id="heading-browser-support-and-polyfills">Browser Support and Polyfills</h2>
<p>As an ES2023 feature, <code>Array.prototype.with()</code> is available in the latest versions of modern browsers and Node.js. [2, 6] For older environments, you'll need to use a transpiler like Babel along with a polyfill (e.g., from <code>core-js</code>) to use it. [5]</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>Array.prototype.with()</code> is a welcome addition for JavaScript developers who value immutability and clean code. It simplifies a common array manipulation pattern, making your code more readable, less error-prone, and more aligned with functional programming principles. It's a small change that can make a big difference in the clarity and robustness of your array operations!</p>
]]></content:encoded></item><item><title><![CDATA[Mastering AI Prompts: A JavaScript/Node.js Developer's Guide to Unlocking AI Power]]></title><description><![CDATA[Artificial Intelligence, particularly Large Language Models (LLMs), is transforming how we work, and software development is no exception. As a JavaScript/Node.js developer, you can leverage AI to generate code, explain complex concepts, debug issues...]]></description><link>https://nurdin.dev/mastering-ai-prompts-a-javascriptnodejs-developers-guide-to-unlocking-ai-power</link><guid isPermaLink="true">https://nurdin.dev/mastering-ai-prompts-a-javascriptnodejs-developers-guide-to-unlocking-ai-power</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[AI]]></category><category><![CDATA[#PromptEngineering]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Wed, 09 Apr 2025 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748040562950/187c47ee-e135-4e26-a9d9-29db34b708ee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Artificial Intelligence, particularly Large Language Models (LLMs), is transforming how we work, and software development is no exception. As a JavaScript/Node.js developer, you can leverage AI to generate code, explain complex concepts, debug issues, write documentation, and much more. The key to unlocking this potential lies in crafting effective <strong>AI prompts</strong>.</p>
<h2 id="heading-what-is-an-ai-prompt">What is an AI Prompt?</h2>
<p>An AI prompt is essentially an instruction or a query you provide to an AI model to elicit a specific response. Think of it as a carefully worded request that guides the AI towards generating the desired output. The quality of the AI's output is heavily dependent on the quality of the prompt. "Garbage in, garbage out" very much applies here.</p>
<h2 id="heading-why-are-good-prompts-crucial">Why are Good Prompts Crucial?</h2>
<p>Well-crafted prompts lead to:</p>
<ul>
<li><p><strong>More Accurate Results:</strong> The AI better understands your intent.</p>
</li>
<li><p><strong>Increased Efficiency:</strong> Less time spent re-prompting or correcting AI outputs.</p>
</li>
<li><p><strong>Relevant Outputs:</strong> Get code, explanations, or solutions that are directly applicable to your needs.</p>
</li>
<li><p><strong>Creative Solutions:</strong> Good prompts can help AI "think outside the box" in useful ways.</p>
</li>
</ul>
<h2 id="heading-best-practices-for-crafting-effective-ai-prompts">Best Practices for Crafting Effective AI Prompts</h2>
<p>Here are some best practices to help you write prompts that get the AI working <em>for</em> you:</p>
<ol>
<li><p><strong>Be Specific and Clear:</strong></p>
<ul>
<li><p>Avoid ambiguity. Clearly state what you want.</p>
</li>
<li><p><strong>Bad:</strong> "Write some JavaScript code."</p>
</li>
<li><p><strong>Good:</strong> "Write a JavaScript function that takes an array of numbers and returns the sum of all even numbers in that array."</p>
</li>
</ul>
</li>
<li><p><strong>Provide Context:</strong></p>
<ul>
<li><p>The more relevant context the AI has, the better its response. This includes the programming language, frameworks, libraries, existing code snippets, or specific constraints.</p>
</li>
<li><p><strong>Bad:</strong> "Fix this error."</p>
</li>
<li><p><strong>Good:</strong> "I'm getting a 'TypeError: Cannot read property 'map' of undefined' in my Node.js Express route. Here's the relevant code snippet: <code>[your code snippet]</code>. What could be causing this?"</p>
</li>
</ul>
</li>
<li><p><strong>Define the Desired Output Format:</strong></p>
<ul>
<li><p>Specify if you want code, a list, a JSON object, a markdown table, an explanation, etc.</p>
</li>
<li><p><strong>Example:</strong> "Generate a JSON object representing a user with 'id', 'username', and 'email' fields. The 'id' should be a UUID."</p>
</li>
</ul>
</li>
<li><p><strong>Use Role-Playing:</strong></p>
<ul>
<li><p>Instruct the AI to act as a specific persona. This can significantly shape the tone and style of the response.</p>
</li>
<li><p><strong>Example:</strong> "You are an expert Node.js performance engineer. Explain three common performance bottlenecks in an Express.js application and suggest solutions for each."</p>
</li>
</ul>
</li>
<li><p><strong>Iterate and Refine:</strong></p>
<ul>
<li>Your first prompt might not be perfect. Don't be afraid to tweak it based on the AI's response. Experiment with different phrasings and levels of detail.</li>
</ul>
</li>
<li><p><strong>Specify Constraints and Requirements:</strong></p>
<ul>
<li><p>Include any limitations, such as "use only ES6 features," "do not use external libraries," "the function should be pure," or "ensure the solution is optimized for memory usage."</p>
</li>
<li><p><strong>Example:</strong> "Write a Node.js function to read a large CSV file line by line without loading the entire file into memory. Do not use any external npm packages other than the built-in <code>fs</code> and <code>readline</code> modules."</p>
</li>
</ul>
</li>
<li><p><strong>Use Examples (Few-Shot Prompting):</strong></p>
<ul>
<li><p>Provide one or more examples of the input and desired output. This helps the AI understand the pattern you're looking for.</p>
</li>
<li><p><strong>Example:</strong></p>
<pre><code class="lang-plaintext">  Convert the following comments to JSDoc format:

  Comment:
  // takes two numbers and returns their sum
  // num1: the first number
  // num2: the second number
  JSDoc:
  /**
   * Takes two numbers and returns their sum.
   * @param {number} num1 - The first number.
   * @param {number} num2 - The second number.
   * @returns {number} The sum of num1 and num2.
   */

  Comment:
  // simple function to greet a user
  // name: the user's name, should be a string
  // returns a greeting string
  JSDoc:
  [AI completes this part]
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Control for Tone and Style:</strong></p>
<ul>
<li><p>Specify the desired tone (e.g., "explain this simply," "provide a formal explanation," "write in a friendly tone").</p>
</li>
<li><p><strong>Example:</strong> "Explain the Node.js event loop like I'm five years old."</p>
</li>
</ul>
</li>
<li><p><strong>Break Down Complex Tasks:</strong></p>
<ul>
<li>If you have a complex problem, break it down into smaller, manageable sub-tasks and prompt the AI for each part.</li>
</ul>
</li>
</ol>
<h2 id="heading-useful-ai-prompt-examples-for-javascriptnodejs-developers">Useful AI Prompt Examples for JavaScript/Node.js Developers</h2>
<p>Here are some practical examples you can adapt:</p>
<h3 id="heading-1-code-generation">1. Code Generation</h3>
<ul>
<li><p><strong>Generating a Utility Function:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Write a JavaScript ES6 function called `debounce` that takes a function `func` and a `delay` in milliseconds.
  The `debounce` function should return a new function that, when called, will only execute `func` after `delay` milliseconds have passed without any new calls to the debounced function.
  Include JSDoc comments for the function and its parameters.
</code></pre>
</li>
<li><p><strong>Generating an Express.js Route:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Create a Node.js Express GET route for `/api/users/:id`.
  This route should:
  1. Extract the `id` parameter from the request.
  2. Assume there's an asynchronous function `findUserById(id)` that returns a Promise resolving to a user object or null if not found.
  3. If the user is found, respond with a 200 OK status and the user object as JSON.
  4. If the user is not found, respond with a 404 Not Found status and a JSON message: `{ error: "User not found" }`.
  5. If `findUserById` throws an error, respond with a 500 Internal Server Error status and a JSON message: `{ error: "Internal server error" }`.
  Use async/await.
</code></pre>
</li>
</ul>
<h3 id="heading-2-code-explanationdocumentation">2. Code Explanation/Documentation</h3>
<ul>
<li><p><strong>Explaining a Code Snippet:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Explain the following JavaScript code snippet, focusing on the use of closures and its practical implications:
</code></pre>
<pre><code class="lang-javascript">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createCounter</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
      count++;
      <span class="hljs-keyword">return</span> count;
    };
  }
  <span class="hljs-keyword">const</span> counter1 = createCounter();
  <span class="hljs-built_in">console</span>.log(counter1()); <span class="hljs-comment">// 1</span>
  <span class="hljs-built_in">console</span>.log(counter1()); <span class="hljs-comment">// 2</span>
</code></pre>
</li>
<li><p><strong>Generating JSDoc Comments:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Generate JSDoc comments for the following JavaScript function. Ensure all parameters and the return value are documented, including their types.
</code></pre>
<pre><code class="lang-javascript">  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserData</span>(<span class="hljs-params">userId, apiToken</span>) </span>{
    <span class="hljs-keyword">if</span> (!userId || !apiToken) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'User ID and API token are required.'</span>);
    }
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://api.example.com/users/<span class="hljs-subst">${userId}</span>`</span>, {
        <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${apiToken}</span>`</span> }
      });
      <span class="hljs-keyword">if</span> (!response.ok) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`API request failed with status <span class="hljs-subst">${response.status}</span>`</span>);
      }
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> response.json();
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to fetch user data:'</span>, error);
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
  }
</code></pre>
</li>
</ul>
<h3 id="heading-3-debugging-assistance">3. Debugging Assistance</h3>
<ul>
<li><p><strong>Identifying Potential Bugs:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  I have this Node.js code that's supposed to update a document in MongoDB using Mongoose, but it's not working as expected. The `updatedAt` field isn't changing. What could be wrong?
</code></pre>
<pre><code class="lang-javascript">  <span class="hljs-comment">// Assuming 'Item' is a Mongoose model</span>
  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateItemName</span>(<span class="hljs-params">itemId, newName</span>) </span>{
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> Item.findById(itemId);
      <span class="hljs-keyword">if</span> (!item) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Item not found'</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
      item.name = newName;
      <span class="hljs-comment">// Is something missing here for the 'updatedAt' field if it's not automatically handled by timestamps:true?</span>
      <span class="hljs-keyword">await</span> item.save();
      <span class="hljs-keyword">return</span> item;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error updating item:'</span>, error);
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
  }
</code></pre>
</li>
</ul>
<h3 id="heading-4-test-case-generation">4. Test Case Generation</h3>
<ul>
<li><p><strong>Generating Unit Tests (e.g., with Jest):</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Write Jest unit tests for the following JavaScript function. Cover edge cases, including empty input, invalid input types, and a typical valid case.
</code></pre>
<pre><code class="lang-javascript">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateTotalPrice</span>(<span class="hljs-params">items</span>) </span>{
    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">Array</span>.isArray(items)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Input must be an array.'</span>);
    }
    <span class="hljs-keyword">return</span> items.reduce(<span class="hljs-function">(<span class="hljs-params">total, item</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> item.price !== <span class="hljs-string">'number'</span> || <span class="hljs-keyword">typeof</span> item.quantity !== <span class="hljs-string">'number'</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Each item must have a numeric price and quantity.'</span>);
      }
      <span class="hljs-keyword">return</span> total + (item.price * item.quantity);
    }, <span class="hljs-number">0</span>);
  }
</code></pre>
</li>
</ul>
<h3 id="heading-5-code-refactoringoptimization">5. Code Refactoring/Optimization</h3>
<ul>
<li><p><strong>Refactoring for Readability or Performance:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Refactor the following JavaScript code to use modern ES6+ features (like async/await instead of .then().catch(), and arrow functions where appropriate). Also, suggest any potential performance improvements if applicable.
</code></pre>
<pre><code class="lang-javascript">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateTotalPrice</span>(<span class="hljs-params">items</span>) </span>{
    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">Array</span>.isArray(items)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Input must be an array.'</span>);
    }
    <span class="hljs-keyword">return</span> items.reduce(<span class="hljs-function">(<span class="hljs-params">total, item</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> item.price !== <span class="hljs-string">'number'</span> || <span class="hljs-keyword">typeof</span> item.quantity !== <span class="hljs-string">'number'</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Each item must have a numeric price and quantity.'</span>);
      }
      <span class="hljs-keyword">return</span> total + (item.price * item.quantity);
    }, <span class="hljs-number">0</span>);
  }
</code></pre>
</li>
</ul>
<h3 id="heading-6-generating-boilerplate">6. Generating Boilerplate</h3>
<ul>
<li><p><strong>Creating a</strong> <code>package.json</code>:</p>
<pre><code class="lang-plaintext">  Prompt:
  Generate a basic `package.json` file for a new Node.js project named "my-api-service".
  It should include:
  - `express` as a dependency.
  - `nodemon` and `jest` as development dependencies.
  - A `start` script that runs `node server.js`.
  - A `dev` script that runs `nodemon server.js`.
  - A `test` script that runs `jest`.
  Set the main entry point to `server.js`.
</code></pre>
</li>
<li><p><strong>Creating a Dockerfile for a Node.js App:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Write a multi-stage Dockerfile for a production Node.js Express application.
  It should:
  1. Use an official Node.js Alpine image (e.g., node:18-alpine).
  2. Copy `package.json` and `package-lock.json` first and install dependencies to leverage Docker caching.
  3. Copy the rest of the application code.
  4. If there's a build step (e.g., `npm run build` for TypeScript), include it in a builder stage.
  5. The final image should only contain production dependencies and the built application code.
  6. Expose port 3000.
  7. Set the default command to start the application (e.g., `node dist/server.js` or `node server.js`).
</code></pre>
</li>
</ul>
<h3 id="heading-7-api-designdocumentation">7. API Design/Documentation</h3>
<ul>
<li><p><strong>Generating OpenAPI (Swagger) Snippets:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Generate an OpenAPI 3.0 specification snippet in YAML for a POST request to `/items`.
  The request body should be a JSON object with a required `name` (string) and an optional `description` (string).
  A successful response should be a 201 status code with the created item object in the response body, including an `id` (string, UUID format).
</code></pre>
</li>
</ul>
<h3 id="heading-8-learning-and-exploration">8. Learning and Exploration</h3>
<ul>
<li><p><strong>Understanding Concepts:</strong></p>
<pre><code class="lang-plaintext">  Prompt:
  Explain the concept of streams in Node.js. What are their benefits, and provide a simple example of using a readable stream to process a file.
</code></pre>
</li>
</ul>
<h2 id="heading-tools-and-platforms">Tools and Platforms</h2>
<p>You'll typically use these prompts with:</p>
<ul>
<li><p><strong>AI Coding Assistants:</strong> GitHub Copilot, Tabnine, Amazon CodeWhisperer, etc. (often integrated into your IDE).</p>
</li>
<li><p><strong>Chat-based LLMs:</strong> ChatGPT, Gemini, Claude, etc. (via their web interfaces or APIs).</p>
</li>
<li><p><strong>LLM APIs:</strong> OpenAI API, Google AI (Gemini API), Anthropic API, etc., for programmatic access in your own tools or applications.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Mastering the art of AI prompting is like learning a new way to communicate with an incredibly powerful assistant. For JavaScript/Node.js developers, this skill can significantly accelerate development, improve code quality, and aid in understanding complex systems. By following these best practices and experimenting with different prompting techniques, you can effectively harness the power of AI to become a more productive and efficient developer. Start prompting, start iterating, and unlock new levels of coding prowess!</p>
]]></content:encoded></item><item><title><![CDATA[Revolutionizing Graceful Shutdown with Enhanced Process Lifecycle Hooks]]></title><description><![CDATA[In the fast-paced world of backend development, application reliability and resilience are paramount. For Node.js developers, ensuring that applications shut down gracefully – finishing ongoing requests, closing database connections, and saving state...]]></description><link>https://nurdin.dev/revolutionizing-graceful-shutdown-with-enhanced-process-lifecycle-hooks</link><guid isPermaLink="true">https://nurdin.dev/revolutionizing-graceful-shutdown-with-enhanced-process-lifecycle-hooks</guid><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Mon, 03 Mar 2025 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748035946965/27889b16-dab7-4eda-b492-ca8304a6727a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the fast-paced world of backend development, application reliability and resilience are paramount. For Node.js developers, ensuring that applications shut down gracefully – finishing ongoing requests, closing database connections, and saving state – has often involved boilerplate code and careful orchestration. Today, we're diving into a significant enhancement in the Node.js 25 release line: <strong>Enhanced Process Lifecycle Hooks</strong>, a feature designed to standardize and simplify graceful shutdowns.</p>
<h2 id="heading-the-lingering-challenge-of-graceful-exits">The Lingering Challenge of Graceful Exits</h2>
<p>Traditionally, Node.js developers have relied on <code>process.on('SIGINT', ...)</code> and <code>process.on('SIGTERM', ...)</code> to catch termination signals. While functional, this approach has several drawbacks:</p>
<ol>
<li><p><strong>Manual Coordination:</strong> Developers are responsible for manually tracking open connections, ongoing asynchronous operations, and ensuring they complete before exiting. This can be complex and error-prone.</p>
</li>
<li><p><strong>Boilerplate:</strong> Every application requiring graceful shutdown needs similar, often verbose, signal handling logic.</p>
</li>
<li><p><strong>Inconsistent Library Support:</strong> Third-party libraries might not always offer straightforward ways to participate in a custom shutdown sequence.</p>
</li>
<li><p><strong>Timeout Management:</strong> Implementing a reliable timeout for the shutdown process itself requires additional logic.</p>
</li>
</ol>
<p>These challenges can lead to abrupt terminations, data corruption, or resource leaks, especially in complex microservices or stateful applications.</p>
<h2 id="heading-introducing-enhanced-process-lifecycle-hooks-in-nodejs-25">Introducing Enhanced Process Lifecycle Hooks in Node.js 25</h2>
<p>Node.js 25 (specifically, starting from v25.1.0, released earlier this year) introduces a more robust and developer-friendly mechanism for managing the application lifecycle, particularly around shutdowns. This isn't a new module but rather an enhancement to the existing <code>process</code> object, making it intuitive and easy to adopt.</p>
<p>The core of this new feature revolves around two main additions:</p>
<ol>
<li><p><code>process.addShutdownHandler(handlerFunction)</code>: Registers an asynchronous function to be executed when the application is requested to shut down.</p>
</li>
<li><p><code>process.registerCriticalTask(promise, description)</code>: Informs Node.js about a critical asynchronous task that must complete before the application fully exits.</p>
</li>
</ol>
<p>Let's explore how these work.</p>
<h3 id="heading-processaddshutdownhandlerasync-signal-exitcode-gt"><code>process.addShutdownHandler(async (signal, exitCode) =&gt; { ... })</code></h3>
<p>This new method allows you to register multiple asynchronous handlers that will be invoked when Node.js initiates a graceful shutdown. This can be triggered by:</p>
<ul>
<li><p>Receiving <code>SIGINT</code> (e.g., Ctrl+C) or <code>SIGTERM</code> signals.</p>
</li>
<li><p>A call to <code>process.exit()</code> if no exit code is provided or if it's called in a way that implies a graceful attempt first (behavior under discussion for future refinements, but signals are the primary trigger for now).</p>
</li>
</ul>
<p>Key characteristics:</p>
<ul>
<li><p><strong>Asynchronous:</strong> Handlers can be <code>async</code> functions and return Promises. Node.js will wait for these promises to resolve (or reject).</p>
</li>
<li><p><strong>Concurrent Execution:</strong> Registered shutdown handlers are executed concurrently, allowing for faster cleanup if tasks are independent.</p>
</li>
<li><p><strong>Signal Information:</strong> The handler receives the <code>signal</code> (e.g., <code>'SIGINT'</code>) and the <code>proposedExitCode</code> that Node.js intends to use.</p>
</li>
<li><p><strong>Order:</strong> While execution is concurrent, registration order is maintained for invocation initiation, though completion depends on each handler's async nature.</p>
</li>
</ul>
<h3 id="heading-processregistercriticaltaskpromise-description"><code>process.registerCriticalTask(promise, description)</code></h3>
<p>Often, your shutdown handlers might initiate cleanup tasks (like closing a database pool), but other parts of your application might also have ongoing work (e.g., finishing writing a large file, completing an outbound API call) that needs to finish.</p>
<p><code>process.registerCriticalTask(promise, description)</code> allows any part of your application to signal that a specific asynchronous operation is critical and the shutdown should wait for it.</p>
<ul>
<li><p><code>promise</code>: The Promise representing the critical operation. The shutdown process will wait for this promise to settle.</p>
</li>
<li><p><code>description</code> (optional): A string describing the task, useful for debugging and logging, especially if a task delays shutdown excessively.</p>
</li>
</ul>
<p>Node.js will keep track of all registered critical tasks. During a graceful shutdown, after invoking shutdown handlers, it will wait for all these critical tasks to complete before finally exiting.</p>
<h3 id="heading-the-graceful-shutdown-process-flow">The Graceful Shutdown Process Flow</h3>
<ol>
<li><p>A shutdown signal (<code>SIGINT</code>, <code>SIGTERM</code>) is received.</p>
</li>
<li><p>Node.js emits a <code>'beforeShutdown'</code> event on <code>process</code> (a new informational event).</p>
</li>
<li><p>All registered shutdown handlers via <code>process.addShutdownHandler()</code> are invoked concurrently.</p>
</li>
<li><p>Node.js waits for all shutdown handler promises to settle.</p>
</li>
<li><p>Node.js then waits for all promises registered via <code>process.registerCriticalTask()</code> to settle.</p>
</li>
<li><p>A configurable global shutdown timeout (e.g., <code>process.SHUTDOWN_TIMEOUT_MS</code>, defaulting to something like 30 seconds) applies to the entire process from step 3. If the timeout is exceeded, Node.js will force exit.</p>
</li>
<li><p>The application exits with the appropriate exit code.</p>
</li>
</ol>
<h2 id="heading-practical-implementation-before-and-after">Practical Implementation: Before and After</h2>
<p>Let's illustrate with a common scenario: an HTTP server that needs to stop accepting new connections, finish processing existing requests, and close its database connection.</p>
<h3 id="heading-before-manual-signal-handling-simplified">Before: Manual Signal Handling (Simplified)</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.js (Old Way)</span>
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> { Pool } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg'</span>); <span class="hljs-comment">// Example DB</span>

<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> Pool({ <span class="hljs-attr">connectionString</span>: <span class="hljs-string">'postgresql://user:pass@host:port/db'</span> });
<span class="hljs-keyword">const</span> server = http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-comment">// Simulate some work</span>
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
    res.writeHead(<span class="hljs-number">200</span>, { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'text/plain'</span> });
    res.end(<span class="hljs-string">'Hello World\n'</span>);
  }, <span class="hljs-number">100</span>);
});

<span class="hljs-keyword">let</span> isShuttingDown = <span class="hljs-literal">false</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gracefulShutdown</span>(<span class="hljs-params">signal</span>) </span>{
  <span class="hljs-keyword">if</span> (isShuttingDown) <span class="hljs-keyword">return</span>;
  isShuttingDown = <span class="hljs-literal">true</span>;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`\nReceived <span class="hljs-subst">${signal}</span>. Shutting down gracefully...`</span>);

  server.close(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'HTTP server closed.'</span>);
    pool.end(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Database pool closed.'</span>);
      process.exit(<span class="hljs-number">0</span>);
    });
  });

  <span class="hljs-comment">// Force shutdown after a timeout</span>
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Could not close connections in time, forcing shutdown.'</span>);
    process.exit(<span class="hljs-number">1</span>);
  }, <span class="hljs-number">10000</span>); <span class="hljs-comment">// 10s timeout</span>
}

process.on(<span class="hljs-string">'SIGINT'</span>, <span class="hljs-function">() =&gt;</span> gracefulShutdown(<span class="hljs-string">'SIGINT'</span>));
process.on(<span class="hljs-string">'SIGTERM'</span>, <span class="hljs-function">() =&gt;</span> gracefulShutdown(<span class="hljs-string">'SIGTERM'</span>));

server.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server listening on port 3000'</span>);
});
</code></pre>
<p>This approach works but involves manual state (<code>isShuttingDown</code>), nested callbacks, and manual timeout management.</p>
<h3 id="heading-after-using-enhanced-process-lifecycle-hooks-nodejs-25">After: Using Enhanced Process Lifecycle Hooks (Node.js 25+)</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.js (New Way with Node.js 25+)</span>
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> { Pool } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg'</span>); <span class="hljs-comment">// Example DB</span>

<span class="hljs-comment">// Hypothetical: Configure global shutdown timeout (e.g., in an init file)</span>
<span class="hljs-comment">// process.SHUTDOWN_TIMEOUT_MS = 20000; // 20 seconds</span>

<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> Pool({ <span class="hljs-attr">connectionString</span>: <span class="hljs-string">'postgresql://user:pass@host:port/db'</span> });
<span class="hljs-keyword">const</span> server = http.createServer(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-comment">// Simulate some work</span>
  <span class="hljs-keyword">const</span> workPromise = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve</span>) =&gt;</span>
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      res.writeHead(<span class="hljs-number">200</span>, { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'text/plain'</span> });
      res.end(<span class="hljs-string">'Hello World\n'</span>);
      resolve();
    }, <span class="hljs-number">100</span>)
  );
  <span class="hljs-comment">// Register this request handling as a critical task</span>
  process.registerCriticalTask(workPromise, <span class="hljs-string">`Request to <span class="hljs-subst">${req.url}</span>`</span>);
});

<span class="hljs-comment">// Shutdown handler for the HTTP server</span>
process.addShutdownHandler(<span class="hljs-keyword">async</span> (signal, exitCode) =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`\nReceived <span class="hljs-subst">${signal}</span> (exit code <span class="hljs-subst">${exitCode}</span>). Closing HTTP server...`</span>);
  <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    server.close(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (err) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error closing HTTP server:'</span>, err);
        <span class="hljs-keyword">return</span> reject(err);
      }
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'HTTP server closed.'</span>);
      resolve();
    });
    <span class="hljs-comment">// Optional: Stop accepting new connections immediately</span>
    <span class="hljs-comment">// server.closeIdleConnections(); // if available</span>
  });
});

<span class="hljs-comment">// Shutdown handler for the database pool</span>
process.addShutdownHandler(<span class="hljs-keyword">async</span> (signal) =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`(<span class="hljs-subst">${signal}</span>) Closing database pool...`</span>);
  <span class="hljs-keyword">await</span> pool.end();
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Database pool closed.'</span>);
});

<span class="hljs-comment">// Example of a long-running task not directly tied to a handler</span>
<span class="hljs-keyword">const</span> someBackgroundTask = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Background task started...'</span>);
  <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">2000</span>)); <span class="hljs-comment">// Simulate work</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Background task finished.'</span>);
};
<span class="hljs-comment">// If this task needs to complete during shutdown, register it:</span>
<span class="hljs-keyword">const</span> backgroundTaskPromise = someBackgroundTask();
process.registerCriticalTask(backgroundTaskPromise, <span class="hljs-string">'Critical background processing'</span>);


server.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server listening on port 3000. Try Ctrl+C to test shutdown.'</span>);
});

<span class="hljs-comment">// You can also listen to the informational event</span>
process.on(<span class="hljs-string">'beforeShutdown'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Node.js is preparing for a graceful shutdown.'</span>);
});
</code></pre>
<p>In the "After" example:</p>
<ul>
<li><p>No manual signal catching (<code>process.on('SIGINT', ...)</code>).</p>
</li>
<li><p>Cleanup logic is encapsulated in <code>async</code> shutdown handlers.</p>
</li>
<li><p>Individual request processing can be registered as critical tasks, ensuring they complete.</p>
</li>
<li><p>Node.js core manages the overall shutdown timeout and orchestration.</p>
</li>
</ul>
<h2 id="heading-key-benefits">Key Benefits</h2>
<p>The introduction of these enhanced lifecycle hooks brings several advantages:</p>
<ol>
<li><p><strong>Standardization:</strong> Provides a unified, built-in mechanism for graceful shutdown across the Node.js ecosystem.</p>
</li>
<li><p><strong>Simplicity &amp; Readability:</strong> Reduces boilerplate code significantly, making application logic cleaner and easier to understand.</p>
</li>
<li><p><strong>Improved Reliability:</strong> Offers better guarantees that critical operations complete before exit, reducing data loss or corruption risks.</p>
</li>
<li><p><strong>Composability:</strong> Libraries can easily participate in the shutdown sequence by registering their own handlers or critical tasks without conflicting with application-level logic.</p>
</li>
<li><p><strong>Centralized Timeout Management:</strong> Node.js handles the overall shutdown timeout, preventing indefinite hangs.</p>
</li>
</ol>
<h2 id="heading-considerations-and-future-scope">Considerations and Future Scope</h2>
<ul>
<li><p><strong>Handler Errors:</strong> If a shutdown handler rejects, Node.js will log the error but continue processing other handlers and critical tasks. The application will still attempt to shut down gracefully.</p>
</li>
<li><p><code>process.exit(code)</code> Behavior: The interaction of <code>process.exit(code)</code> with these new hooks is an area of ongoing discussion. The primary focus for now is signal-initiated shutdowns. Forcing an immediate exit will likely still bypass these hooks.</p>
</li>
<li><p><strong>Worker Threads:</strong> The integration with worker threads and how they participate in this lifecycle is an important consideration for future enhancements. Currently, these hooks are primarily for the main thread.</p>
</li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>This feature is available in Node.js 25.1.0 and later versions (part of the "Current" release line as of May 2025). To use it, simply update your Node.js runtime and start leveraging <code>process.addShutdownHandler()</code> and <code>process.registerCriticalTask()</code> in your applications.</p>
<p>No special flags are needed; it's a core enhancement to the <code>process</code> global.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The enhanced process lifecycle hooks in Node.js 25 mark a significant step forward in building robust, production-grade applications. By standardizing and simplifying graceful shutdown, Node.js empowers developers to write more resilient services with less effort. This feature addresses a long-standing need in the community and is a testament to Node.js's continued evolution towards better developer experience and operational excellence.</p>
<p>As you migrate or build new applications on Node.js 25, incorporating these new hooks should be a priority for ensuring your services can handle termination signals gracefully and reliably. The future of Node.js application resilience looks brighter than ever.</p>
]]></content:encoded></item><item><title><![CDATA[Schedule a Job At The Same Time In Different Timezones]]></title><description><![CDATA[The Story
Imagine you have an application and your users from different timezones and you need to send a message every day at 6:00 o'clock for each user to say Good Morning! ☕️, very simple right? let's see...
The First Try
The first and obvious solu...]]></description><link>https://nurdin.dev/schedule-a-job-at-the-same-time-in-different-timezones</link><guid isPermaLink="true">https://nurdin.dev/schedule-a-job-at-the-same-time-in-different-timezones</guid><category><![CDATA[MongoDB]]></category><category><![CDATA[cronjob]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Wed, 21 Dec 2022 07:38:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4e3f0bc9270867612eb87c7ff74b2d9b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-story">The Story</h2>
<p>Imagine you have an application and your users from different timezones and you need to send a message every day at 6:00 o'clock for each user to say Good Morning! ☕️, very simple right? let's see...</p>
<h3 id="heading-the-first-try">The First Try</h3>
<p>The first and obvious solution is to run a cronjob <code>0 6 * * *</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671738981693/7cb25c84-51f7-44f1-a927-3ca1989ce2bc.png" alt class="image--center mx-auto" /></p>
<p>The code above will run every day at 6:00 and send a message to each user, which is what we need, right!? yes and no 😅</p>
<p><strong>Yes</strong>, because only the users on the same server timezone will receive a message at the expected time.</p>
<p><strong>No</strong>, because the users in the other timezone will receive a message at the wrong time.</p>
<h3 id="heading-how-to-fix-this">How to fix this?</h3>
<p>The idea popped into my mind after I saw this <a target="_blank" href="https://stackoverflow.com/a/49814813/4489568">answer</a> on StackOverflow, and because I'm using MongoDB in my <a target="_blank" href="https://github.com/NurdinDev/Adhan-Slack-App">project</a>, I'll explain my approach to how I solved this issue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671741814800/7f9d9cb5-7bc6-4fdf-b003-7dfc92c9546d.png" alt class="image--center mx-auto" /></p>
<p>The code above runs every half hour and queries all users who have time at 6:00 by using MongoDB date query.</p>
<p>Done 😉</p>
<p><strong>btw, you should store the timezone of the user in the database.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Get Your System Info From Terminal]]></title><description><![CDATA[Sometimes we need to know the system version and more details about the kernel version, you might need this information for instance if you report a bug and you want to mention what is the version of the system you are using in order to reproduce the...]]></description><link>https://nurdin.dev/get-os-info-from-terminal</link><guid isPermaLink="true">https://nurdin.dev/get-os-info-from-terminal</guid><category><![CDATA[command line]]></category><category><![CDATA[terminal]]></category><category><![CDATA[macOS]]></category><dc:creator><![CDATA[Nureddin Badawi]]></dc:creator><pubDate>Wed, 23 Nov 2022 18:45:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/475bbc98e92d06b16c27aec55dc3d92c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes we need to know the system version and more details about the kernel version, you might need this information for instance if you report a bug and you want to mention what is the version of the system you are using in order to reproduce the issue.</p>
<p><code>sw_vers</code>: Print macOS operating system version information, More information: <a target="_blank" href="https://ss64.com/osx/sw_vers.html">https://ss64.com/osx/sw_vers.html</a>.</p>
<p><code>uname</code>: Print kernel name.</p>
<p>Combine them together and get this result <code>sw_vers &amp;&amp; uname -v</code> on macOS/Linux or <code>ver</code> on Windows OS</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669295089658/iOF_KAD_o.png" alt="Screenshot 2022-11-24 at 12.44.07 PM.png" /></p>
]]></content:encoded></item></channel></rss>