<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: qudrat ullah</title>
    <description>The latest articles on Forem by qudrat ullah (@qudratullahdev).</description>
    <link>https://forem.com/qudratullahdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3728686%2F67a54c8a-720d-4dcc-ae85-7ed7d072a5fe.jpg</url>
      <title>Forem: qudrat ullah</title>
      <link>https://forem.com/qudratullahdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/qudratullahdev"/>
    <language>en</language>
    <item>
      <title>How Hackers Are Bypassing cPanel 2FA and What You Must Do Now</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Fri, 01 May 2026 07:42:39 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/how-hackers-are-bypassing-cpanel-2fa-and-what-you-must-do-now-29km</link>
      <guid>https://forem.com/qudratullahdev/how-hackers-are-bypassing-cpanel-2fa-and-what-you-must-do-now-29km</guid>
      <description>&lt;p&gt;As engineers, we rely on layers of security to protect our work. One of the most trusted layers is two-factor authentication (2FA). It's the digital deadbolt on our front door. But what happens when that deadbolt can be picked in seconds? A critical vulnerability in cPanel, the web hosting control panel used by millions of websites, is being actively exploited right now. It allows attackers to bypass 2FA entirely.&lt;/p&gt;

&lt;p&gt;This is not a theoretical problem. This is happening in the wild. If you manage or develop for websites hosted on cPanel, you need to understand this threat and act immediately. Let's break down the vulnerability, how it works, and what you need to do to protect your systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Vulnerability (CVE-2023-29489)?
&lt;/h2&gt;

&lt;p&gt;The vulnerability, officially known as CVE-2023-29489, affects cPanel &amp;amp; WHM (WebHost Manager). At its core, it is a flaw in the brute-force protection for the 2FA verification step. &lt;/p&gt;

&lt;p&gt;Here is how it is supposed to work: when you enter your password correctly, the system asks for a 2FA code. If you enter the wrong code a few times (say, 3 or 5 times), the system should lock you out for a period. This is called rate-limiting, and it's a fundamental defense against brute-force attacks.&lt;/p&gt;

&lt;p&gt;The cPanel vulnerability means this rate-limiting was not working correctly. An attacker who already has a valid username and password can try to guess the 2FA code an unlimited number of times, as fast as their computer can send requests. Since a typical 2FA code is just a 6-digit number, there are only one million possible combinations. A modern script can try all of them in minutes.&lt;/p&gt;

&lt;p&gt;Think of it this way: your password is the main lock on a door. 2FA is a second, smaller lock. The flaw means an attacker can try every possible key for that second lock at machine speed without ever being stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Exploit Works Step-by-Step
&lt;/h2&gt;

&lt;p&gt;The attack requires two things: a valid username-password pair and a vulnerable cPanel instance. The first part is usually achieved through phishing or credential stuffing, where attackers use passwords leaked from other data breaches.&lt;/p&gt;

&lt;p&gt;Once they have the credentials, the process is dangerously simple.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkbfyvpgyslq4dopd.public.blob.vercel-storage.com%2Farticles%2Fhow-hackers-are-bypassing-cpanel-2fa-and-what-you-must-do-now%2Fimg-1-8de14e45-0f0f-4961-b17d-5dc66ad59454.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkbfyvpgyslq4dopd.public.blob.vercel-storage.com%2Farticles%2Fhow-hackers-are-bypassing-cpanel-2fa-and-what-you-must-do-now%2Fimg-1-8de14e45-0f0f-4961-b17d-5dc66ad59454.png" alt="A flowchart showing the cPanel exploit. An attacker with a password logs in, then uses a script to loop through 2FA codes. A vulnerable cPanel does not rate limit the attempts, allowing the loop to continue until the correct code is found, granting full access." width="800" height="1442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Obtain Credentials:&lt;/strong&gt; The attacker gets a working username and password for a cPanel account. This is the entry ticket.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Initial Login:&lt;/strong&gt; They use these credentials to log in. The system correctly validates the password and proceeds to the next step: the 2FA challenge.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Brute-Force the 2FA Code:&lt;/strong&gt; The cPanel login form asks for the 6-digit code from the user's authenticator app. Instead of entering one code, the attacker launches a script that sends hundreds or thousands of login requests per second, each with a different 2FA code (from 000000 to 999999).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Bypass and Gain Access:&lt;/strong&gt; Because the rate-limiting is broken, cPanel does not block these repeated attempts. Within a short time, the script hits the correct code. The attacker is authenticated and gains full administrative access to the cPanel account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once inside, they have the keys to the kingdom. They can access databases, read source code, install malware, deface your website, or use your server to attack others.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Simple Brute-Force Simulation
&lt;/h3&gt;

&lt;p&gt;To make this more concrete, here is a simplified Python script that demonstrates the logic of a brute-force attack. This is for educational purposes only, to show how trivial it is to automate these attempts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# WARNING: This code is for educational purposes only.
# Do not use it for any unauthorized or malicious activity.
&lt;/span&gt;
&lt;span class="n"&gt;TARGET_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://your-cpanel-domain.com:2083/login&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stolen_user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stolen_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;# Create a session to persist cookies after password auth
&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Note: A real exploit would first handle the initial password login.
# This example focuses on the 2FA brute-force part.
&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting 2FA brute-force simulation...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Loop through all possible 6-digit codes
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;code_int&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Format the code as a 6-digit string with leading zeros
&lt;/span&gt;    &lt;span class="n"&gt;tfa_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;code_int&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# The payload would contain the 2FA code
&lt;/span&gt;    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# In a real scenario, this might be a session token
&lt;/span&gt;        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tfa_code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tfa_code&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# In a real exploit, the attacker would check the response
&lt;/span&gt;        &lt;span class="c1"&gt;# to see if the login was successful.
&lt;/span&gt;        &lt;span class="c1"&gt;# response = session.post(TARGET_URL, data=payload)
&lt;/span&gt;
        &lt;span class="c1"&gt;# For simulation, we just print progress
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;code_int&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempting code: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tfa_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# if "Login Successful" in response.text:
&lt;/span&gt;        &lt;span class="c1"&gt;#     print(f"\nSuccess! Code found: {tfa_code}")
&lt;/span&gt;        &lt;span class="c1"&gt;#     break
&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Simulation finished.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script loops from 0 to 999,999, formats each number as a 6-digit code, and simulates sending it in a request. A real attacker's script would be more sophisticated, using multiple threads to run much faster, but the core logic is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Must Do Right Now
&lt;/h2&gt;

&lt;p&gt;Protecting your systems requires immediate and layered action. Do not assume you are safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Update cPanel &amp;amp; WHM Immediately
&lt;/h3&gt;

&lt;p&gt;This is the most critical step. The vulnerability was patched by the cPanel team. You must ensure your server is running one of the fixed versions or a newer one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  110.0.13 or later&lt;/li&gt;
&lt;li&gt;  108.0.13 or later&lt;/li&gt;
&lt;li&gt;  102.0.20 or later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you manage your own server, run the update yourself. If you use a managed hosting provider, contact them and confirm they have patched their systems. Do not just assume they have. Ask for confirmation. You can check your cPanel version by logging in and looking at the version number, usually in the footer or sidebar.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Enforce Strong, Unique Passwords
&lt;/h3&gt;

&lt;p&gt;Remember, this exploit &lt;em&gt;requires&lt;/em&gt; the attacker to have a valid password first. The single best thing you can do to prevent this and many other attacks is to use a strong, unique password for your cPanel account. Use a password manager to generate and store it. If you have been reusing a password, change it now.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Monitor Your Logs
&lt;/h3&gt;

&lt;p&gt;Even on a patched system, monitoring for suspicious activity is a good practice. A failed brute-force attack will still leave traces. Look for a massive number of failed login attempts from a single IP address in your cPanel access logs (&lt;code&gt;/usr/local/cpanel/logs/login_log&lt;/code&gt;). A flood of POST requests to the login endpoint is a clear indicator someone is trying to get in.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use a Web Application Firewall (WAF)
&lt;/h3&gt;

&lt;p&gt;A well-configured WAF can provide an extra layer of protection. Tools like ModSecurity (often included with cPanel) or external services like Cloudflare can be configured with rules to detect and block brute-force patterns before they even reach cPanel. For example, you can set a rule to temporarily ban an IP that makes more than 10 failed login attempts in a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Restrict Access by IP Address
&lt;/h3&gt;

&lt;p&gt;For maximum security, you can configure WHM or your server's firewall to only allow logins from specific, trusted IP addresses (like your office or home network). This completely blocks login attempts from anywhere else. The trade-off is convenience. If you need to log in from a new location, you have to add your new IP to the allow-list first. For high-value servers, this is a trade-off worth making.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Lesson: Defense in Depth
&lt;/h2&gt;

&lt;p&gt;This cPanel vulnerability is a powerful reminder that no single security measure is foolproof. 2FA is excellent, but a bug in its implementation can render it useless. This is why we practice "defense in depth".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;A strong password&lt;/strong&gt; would have stopped the attacker from even reaching the 2FA stage.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A patched cPanel&lt;/strong&gt; instance would have correctly rate-limited the 2FA attempts.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A WAF&lt;/strong&gt; might have blocked the attack pattern at the network edge.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;IP whitelisting&lt;/strong&gt; would have prevented the attacker from connecting at all.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Log monitoring&lt;/strong&gt; would help you detect the attempt, even if it failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer provides another chance to stop an attack. When one layer fails, another is there to catch it. As developers and system administrators, our job is not to find a single perfect solution, but to build a resilient system with multiple, overlapping defenses.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://www.qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.qudratullah.net/blog/how-hackers-are-bypassing-cpanel-2fa-and-what-you-must-do-now" rel="noopener noreferrer"&gt;www.qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cpanel</category>
      <category>security</category>
      <category>vulnerability</category>
      <category>2fa</category>
    </item>
    <item>
      <title>Beyond 'any': Mastering TypeScript's Utility Types for Cleaner and Safer Code</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Thu, 30 Apr 2026 22:03:14 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/beyond-any-mastering-typescripts-utility-types-for-cleaner-and-safer-code-3n8p</link>
      <guid>https://forem.com/qudratullahdev/beyond-any-mastering-typescripts-utility-types-for-cleaner-and-safer-code-3n8p</guid>
      <description>&lt;p&gt;As developers, we have all been there. You are working with a complex object, you need to pass just a part of it to a function, and the TypeScript compiler starts complaining. The quick fix? Slap an &lt;code&gt;any&lt;/code&gt; on it and move on. It silences the errors, but it also silences TypeScript's greatest strengths: type safety and autocompletion.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;any&lt;/code&gt; is like turning off the safety features in your car. It might feel liberating for a moment, but it dramatically increases the risk of crashes down the road. Fortunately, TypeScript gives us a powerful and elegant way to solve these problems without resorting to &lt;code&gt;any&lt;/code&gt;. They are called Utility Types.&lt;/p&gt;

&lt;p&gt;These are built-in tools that let you transform existing types into new ones. Think of them as functions for your types. They help you create precise, reusable, and maintainable type definitions that accurately describe your data structures. Let's explore some of the most essential utility types that will make your code cleaner, safer, and easier to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, Let's Understand the &lt;code&gt;any&lt;/code&gt; Problem
&lt;/h2&gt;

&lt;p&gt;When you use &lt;code&gt;any&lt;/code&gt;, you are telling TypeScript to get out of the way. You are effectively opting out of type checking for that variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logUserDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// No autocompletion for user properties here.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namee&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Typo! TypeScript won't catch this.&lt;/span&gt;
  &lt;span class="c1"&gt;// This will crash at runtime with 'undefined'.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code has a typo (&lt;code&gt;namee&lt;/code&gt; instead of &lt;code&gt;name&lt;/code&gt;). With &lt;code&gt;any&lt;/code&gt;, TypeScript can't help you. The error will only appear when you run the code, potentially in front of your users. By using specific types, we move these errors from runtime to compile-time, where they are much cheaper and easier to fix.&lt;/p&gt;

&lt;p&gt;Utility types are your best friends in this journey. They allow you to create those specific types without writing a lot of boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying Properties: &lt;code&gt;Partial&lt;/code&gt;, &lt;code&gt;Required&lt;/code&gt;, and &lt;code&gt;Readonly&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;These three utilities adjust the properties of an existing type, making them optional, mandatory, or immutable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Partial&amp;lt;Type&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Partial&lt;/code&gt; makes all properties of a given &lt;code&gt;Type&lt;/code&gt; optional. This is incredibly useful for functions that perform updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; Imagine you have a &lt;code&gt;User&lt;/code&gt; profile in your application. When a user updates their profile, they probably only send the fields they changed, not their entire profile object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The 'updates' object can have 'name', 'email', 'bio', or any combination.&lt;/span&gt;
  &lt;span class="c1"&gt;// All properties are optional, but they must match the types in User.&lt;/span&gt;
  &lt;span class="c1"&gt;// For example, updates.name must be a string if it exists.&lt;/span&gt;

  &lt;span class="c1"&gt;// ... logic to fetch user by id and apply updates&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage:&lt;/span&gt;
&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A new bio for my profile.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jane.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;Partial&lt;/code&gt;, you would either have to use &lt;code&gt;any&lt;/code&gt; or create a whole new &lt;code&gt;UserUpdate&lt;/code&gt; interface with all optional properties. &lt;code&gt;Partial&amp;lt;User&amp;gt;&lt;/code&gt; keeps your code DRY (Don't Repeat Yourself) by deriving the update type directly from the source of truth, the &lt;code&gt;User&lt;/code&gt; interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Required&amp;lt;Type&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Required&lt;/code&gt; is the opposite of &lt;code&gt;Partial&lt;/code&gt;. It takes a type that might have optional properties and makes all of them required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; Let's say you have a configuration object for your app. Some settings might be optional because you have default values. However, once you have applied the defaults, you want to work with a config object where you know every property exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Optional&lt;/span&gt;
  &lt;span class="nl"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Optional&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;finalConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;userConfig&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Now, inside this function, you can safely access finalConfig.timeout&lt;/span&gt;
  &lt;span class="c1"&gt;// and finalConfig.retries without checking if they are undefined.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Connecting to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with a timeout of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;Required&lt;/code&gt; makes your internal logic simpler and safer because you eliminate the need for constant null or undefined checks.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Readonly&amp;lt;Type&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Readonly&lt;/code&gt; makes all properties of a type read-only. This is a great way to prevent accidental mutations of objects, which is a common source of bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; You have a global configuration or a state object that should not be changed by any part of your application directly. You want to enforce immutability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadSettings&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Readonly&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppSettings&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="c1"&gt;// Object.freeze is a runtime check, Readonly is a compile-time check&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// This will cause a TypeScript error during compilation:&lt;/span&gt;
&lt;span class="c1"&gt;// settings.theme = 'light'; // Error: Cannot assign to 'theme' because it is a read-only property.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps you write more predictable code, especially in larger applications or when working with state management libraries like Redux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shaping Objects: &lt;code&gt;Pick&lt;/code&gt; and &lt;code&gt;Omit&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;These utilities allow you to create new types by selecting or removing properties from an existing type. They are perfect for creating subsets of your models, like for API responses or form data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkbfyvpgyslq4dopd.public.blob.vercel-storage.com%2Farticles%2Fbeyond-any-mastering-typescripts-utility-types-for-cleaner-and-safer-code%2Fimg-1-185b4396-d6c4-4ead-97e3-5d1db7bac26b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkbfyvpgyslq4dopd.public.blob.vercel-storage.com%2Farticles%2Fbeyond-any-mastering-typescripts-utility-types-for-cleaner-and-safer-code%2Fimg-1-185b4396-d6c4-4ead-97e3-5d1db7bac26b.png" alt="A flowchart showing how Pick selects properties 'id' and 'name' from a User type, while Omit removes 'password' and 'createdAt' to create a new type." width="800" height="1709"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Pick&amp;lt;Type, Keys&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Pick&lt;/code&gt; creates a new type by picking a set of properties (&lt;code&gt;Keys&lt;/code&gt;) from an existing &lt;code&gt;Type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; Your main &lt;code&gt;User&lt;/code&gt; model contains sensitive information like a password hash. When you send user data to the client-side, you only want to include public information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;passwordHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Create a type with only the public fields&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserPublicProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UserPublicProfile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much better than creating a separate &lt;code&gt;UserPublicProfile&lt;/code&gt; interface manually. If you ever add a new public field to &lt;code&gt;User&lt;/code&gt; (like &lt;code&gt;avatarUrl&lt;/code&gt;), you just need to add it to the &lt;code&gt;Pick&lt;/code&gt; list, and TypeScript will ensure your &lt;code&gt;getUserProfile&lt;/code&gt; function is updated accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Omit&amp;lt;Type, Keys&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Omit&lt;/code&gt; does the opposite of &lt;code&gt;Pick&lt;/code&gt;. It creates a new type by taking all properties from &lt;code&gt;Type&lt;/code&gt; and then removing a specific set of &lt;code&gt;Keys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; When creating a new user, the client sends all the necessary information except for fields that the server generates, like &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;createdAt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;passwordHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Create a type for the creation payload&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreateUserPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createdAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The payload is guaranteed to have 'name', 'email', and 'passwordHash',&lt;/span&gt;
  &lt;span class="c1"&gt;// but not 'id' or 'createdAt'.&lt;/span&gt;
  &lt;span class="c1"&gt;// ... logic to create user in the database&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage:&lt;/span&gt;
&lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;passwordHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Omit&lt;/code&gt; is often more convenient than &lt;code&gt;Pick&lt;/code&gt; when you want to remove just one or two properties from a large object.&lt;/p&gt;

&lt;h2&gt;
  
  
  For Key-Value Pairs: &lt;code&gt;Record&amp;lt;Keys, Type&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Record&lt;/code&gt; is used to define an object type where the keys are of a specific type and the values are of another specific type. It is perfect for creating dictionaries or maps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; You are building a feature flag system. The keys are the feature names (strings), and the values are booleans indicating if the feature is enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A simple dictionary with string keys&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;featureFlags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-checkout-flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beta-user-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// A more powerful example with a union type for keys&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UiTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeColors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UiTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#007bff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#6c757d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f8f9fa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;Record&amp;lt;UiTheme, string&amp;gt;&lt;/code&gt; is much safer than &lt;code&gt;{ [key: string]: string; }&lt;/code&gt;. It ensures that you can only use the keys defined in the &lt;code&gt;UiTheme&lt;/code&gt; type, preventing typos and ensuring your theme object is always complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining Utility Types for Maximum Power
&lt;/h2&gt;

&lt;p&gt;This is where utility types truly shine. You can chain and nest them to create very specific and powerful types with minimal code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world scenario:&lt;/strong&gt; You are creating a function to update a blog post. The update payload can contain any of the post's properties except for the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;authorId&lt;/code&gt;, which should never be changed. All fields in the payload are optional.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F606vyuqj3o3ufph606hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F606vyuqj3o3ufph606hg.png" alt="A flowchart showing two steps. First, Omit removes 'authorId' and 'createdAt' from a Post type. Second, Partial makes the remaining properties optional to create a PostUpdatePayload." width="800" height="115"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;authorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Let's build the type step-by-step:&lt;/span&gt;
&lt;span class="c1"&gt;// 1. First, remove the immutable properties from Post&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;EditablePost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Result: { title: string; content: string; publishedAt: Date | null; }&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Now, make all properties of the result optional&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PostUpdatePayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EditablePost&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Result: { title?: string; content?: string; publishedAt?: Date | null; }&lt;/span&gt;

&lt;span class="c1"&gt;// You can also write it in one line:&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PostUpdatePayloadOneLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostUpdatePayloadOneLine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// payload can contain { title: 'New Title' } or { content: '...' } etc.&lt;/span&gt;
  &lt;span class="c1"&gt;// payload cannot contain 'id' or 'authorId'. TypeScript will throw an error.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line of code creates a complex, safe, and highly descriptive type. It perfectly documents the contract for your &lt;code&gt;updatePost&lt;/code&gt; function without any extra comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts and Best Practices
&lt;/h2&gt;

&lt;p&gt;TypeScript's utility types are an essential part of any modern developer's toolkit. They help you write code that is more robust, maintainable, and self-documenting.&lt;/p&gt;

&lt;p&gt;Here are a few best practices to keep in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Strive for a Single Source of Truth:&lt;/strong&gt; Define your core models (like &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Post&lt;/code&gt;) once. Use utility types to derive variations for different use cases (APIs, forms, updates). This prevents your types from getting out of sync.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Favor Utility Types Over Manual Interfaces for Derived Types:&lt;/strong&gt; Instead of writing &lt;code&gt;interface UserUpdate { name?: string; email?: string; ... }&lt;/code&gt;, use &lt;code&gt;Partial&amp;lt;User&amp;gt;&lt;/code&gt;. It is less code and automatically stays up-to-date.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Don't Overdo It:&lt;/strong&gt; If your type definition becomes a deeply nested chain like &lt;code&gt;Partial&amp;lt;Readonly&amp;lt;Omit&amp;lt;Pick&amp;lt;...&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, it might be a sign that your logic is too complex. Consider creating a new, named interface for clarity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Escaping the &lt;code&gt;any&lt;/code&gt; trap is a critical step in growing as a TypeScript developer. By mastering utility types, you are not just silencing the compiler. You are leveraging the full power of the type system to build better, safer software.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://www.qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.qudratullah.net/blog/beyond-any-mastering-typescripts-utility-types-for-cleaner-and-safer-code" rel="noopener noreferrer"&gt;www.qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>softwareengineering</category>
      <category>bestpractices</category>
      <category>codequality</category>
    </item>
    <item>
      <title>From Code on Your Laptop to a Universal Box: A Beginner's Guide to Dockerizing Node.js</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Thu, 30 Apr 2026 21:55:02 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/from-code-on-your-laptop-to-a-universal-box-a-beginners-guide-to-dockerizing-nodejs-meo</link>
      <guid>https://forem.com/qudratullahdev/from-code-on-your-laptop-to-a-universal-box-a-beginners-guide-to-dockerizing-nodejs-meo</guid>
      <description>&lt;p&gt;As a software engineer, one of the first frustrating phrases you will hear is, "Well, it works on my machine!" This happens when code runs perfectly on your computer but fails on a colleague's laptop or a production server. The reason is usually a small difference in the environment, like a different Node.js version or a missing system library.&lt;/p&gt;

&lt;p&gt;This is where Docker comes in. Think of Docker as a way to create a standard, universal box for your application. This box contains everything your code needs to run: the code itself, libraries, tools, and settings. You build this box once, and then you can ship it and run it anywhere, and it will always work the same way.&lt;/p&gt;

&lt;p&gt;In this guide, we will take a simple Node.js web server and package it into one of these universal boxes using Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Will Need
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have these two things installed on your computer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Node.js:&lt;/strong&gt; To run our simple application locally first.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Docker Desktop:&lt;/strong&gt; The application that lets you build and run Docker containers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Simple Node.js App
&lt;/h2&gt;

&lt;p&gt;First, we need an application to package. Let's create a very basic web server using Express, a popular Node.js framework.&lt;/p&gt;

&lt;p&gt;Create a new folder for your project. Inside that folder, create two files: &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This file tells Node.js about our project and its dependencies. The only dependency we need is &lt;code&gt;express&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"simple-node-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple Node.js app for Docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.18.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is our actual server code. It creates a web server that listens for requests and sends back a simple message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from my Node.js app!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, open your terminal in the project folder and run these commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Install the dependency: &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Start the server: &lt;code&gt;node index.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you open your web browser and go to &lt;code&gt;http://localhost:3000&lt;/code&gt;, you should see the message "Hello from my Node.js app!".&lt;/p&gt;

&lt;p&gt;Great! Our app works locally. Now let's put it in a box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Understanding Docker Concepts
&lt;/h2&gt;

&lt;p&gt;Before we write the instructions for our box, let's quickly learn three key Docker terms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dockerfile:&lt;/strong&gt; This is a simple text file with a list of instructions. It's like a recipe for building our box. We will write this file ourselves.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Image:&lt;/strong&gt; When you follow the recipe in the Dockerfile, you create an Image. An image is a blueprint. It's a saved, unchangeable package that contains our application and all its needs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Container:&lt;/strong&gt; A container is a running instance of an image. If the image is the blueprint, the container is the actual house built from that blueprint. You can create many containers from a single image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flow is simple: you write a Dockerfile, use it to build an Image, and then run that Image as a Container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxst8020hgipmle7ll6rw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxst8020hgipmle7ll6rw.png" alt="A flowchart showing that a Dockerfile is used with the 'docker build' command to create a Docker Image. The Docker Image is then used with the 'docker run' command to create multiple running Containers." width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Writing Your First Dockerfile
&lt;/h2&gt;

&lt;p&gt;In the same project folder, create a new file named &lt;code&gt;Dockerfile&lt;/code&gt; (no extension, just that name).&lt;/p&gt;

&lt;p&gt;This file will contain the step-by-step instructions for Docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start from an official Node.js image.&lt;/span&gt;
&lt;span class="c"&gt;# The 'alpine' version is very small, which is great.&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Create and set the working directory inside the container.&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package.json and package-lock.json first.&lt;/span&gt;
&lt;span class="c"&gt;# This helps Docker use its cache smartly.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install the application dependencies inside the container.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Now, copy the rest of your application's source code.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Tell Docker that the container listens on port 3000.&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# The command to run when the container starts.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down line by line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;FROM node:18-alpine&lt;/code&gt;: Every Docker image starts from a base image. Here, we start with an official image that already has Node.js version 18 installed on a minimal version of Linux called Alpine.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;WORKDIR /app&lt;/code&gt;: This sets the default location inside the container for all subsequent commands. It's like running &lt;code&gt;cd /app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;COPY package*.json ./&lt;/code&gt;: We copy our package files into the &lt;code&gt;/app&lt;/code&gt; directory. We do this before copying our code. This is a smart trick. Docker builds in layers. If our code changes but &lt;code&gt;package.json&lt;/code&gt; does not, Docker can reuse the &lt;code&gt;npm install&lt;/code&gt; layer from a previous build, which saves a lot of time.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;RUN npm install&lt;/code&gt;: This runs the command to install our dependencies inside the container.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;COPY . .&lt;/code&gt;: Now we copy the rest of our files (like &lt;code&gt;index.js&lt;/code&gt;) into the container.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;EXPOSE 3000&lt;/code&gt;: This is like a piece of documentation. It tells Docker that our application inside the container will be using port 3000. It doesn't actually open the port to the outside world.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;CMD ["node", "index.js"]&lt;/code&gt;: This is the final command that will be executed when the container starts. It runs our app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Build the Image and Run the Container
&lt;/h2&gt;

&lt;p&gt;Now for the magic part. Go back to your terminal, make sure you are in your project directory, and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The -t flag lets you 'tag' or name your image.&lt;/span&gt;
&lt;span class="c"&gt;# The '.' at the end tells Docker to look for the Dockerfile in the current directory.&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-node-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker will now execute the steps in your &lt;code&gt;Dockerfile&lt;/code&gt;. You will see it downloading the base image and running your commands. Once it's finished, you have a Docker image named &lt;code&gt;my-node-app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's run it as a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4000:3000 my-node-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's understand this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;docker run&lt;/code&gt;: The command to start a container.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;-p 4000:3000&lt;/code&gt;: This is the port mapping. It connects port 4000 on your computer (the host) to port 3000 inside the container. Remember, &lt;code&gt;EXPOSE 3000&lt;/code&gt; only documented the port. This &lt;code&gt;-p&lt;/code&gt; flag actually opens it up.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;my-node-app&lt;/code&gt;: The name of the image we want to run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, open your browser and go to &lt;code&gt;http://localhost:4000&lt;/code&gt;. You will see the same message: "Hello from my Node.js app!".&lt;/p&gt;

&lt;p&gt;The difference is that this time, the app is not running directly on your machine. It is running inside a completely isolated Docker container.&lt;/p&gt;

&lt;p&gt;To stop the container, go to your terminal and press &lt;code&gt;Ctrl + C&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Tip: The &lt;code&gt;.dockerignore&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;Just like &lt;code&gt;.gitignore&lt;/code&gt;, you can create a &lt;code&gt;.dockerignore&lt;/code&gt; file to tell Docker which files and folders to ignore when copying your code into the image. This keeps your image small and secure.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;.dockerignore&lt;/code&gt; and add this to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
npm-debug.log
Dockerfile
.dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We especially want to ignore &lt;code&gt;node_modules&lt;/code&gt; because we run &lt;code&gt;npm install&lt;/code&gt; inside the container to get a fresh copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Congratulations! You have just packaged your first application with Docker.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Docker solves the "it works on my machine" problem&lt;/strong&gt; by packaging your app and its environment into a single container.&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/strong&gt; is a recipe for building a Docker &lt;strong&gt;Image&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;Container&lt;/strong&gt; is a running instance of an Image.&lt;/li&gt;
&lt;li&gt;  Structure your &lt;code&gt;Dockerfile&lt;/code&gt; to copy &lt;code&gt;package.json&lt;/code&gt; and run &lt;code&gt;npm install&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; you copy your source code. This makes your builds much faster.&lt;/li&gt;
&lt;li&gt;  Use the &lt;code&gt;docker build&lt;/code&gt; command to create an image and &lt;code&gt;docker run&lt;/code&gt; to start a container from it.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;-p&lt;/code&gt; flag is essential for connecting a port on your machine to a port inside the container, allowing you to access your app.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://www.qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.qudratullah.net/blog/from-code-on-your-laptop-to-a-universal-box-a-beginners-guide-to-dockerizing-nodejs" rel="noopener noreferrer"&gt;www.qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Beyond the Origin: How Cloudflare Workers Forge High-Performance APIs</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Thu, 30 Apr 2026 21:53:07 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/beyond-the-origin-how-cloudflare-workers-forge-high-performance-apis-3k2a</link>
      <guid>https://forem.com/qudratullahdev/beyond-the-origin-how-cloudflare-workers-forge-high-performance-apis-3k2a</guid>
      <description>&lt;p&gt;As engineers, we spend a lot of time optimizing our origin servers. We scale them up, add more instances, and fine-tune our database queries. But what if the biggest performance gain wasn't on our origin server at all? What if it was somewhere between our user and our server?&lt;/p&gt;

&lt;p&gt;For years, the model has been simple: a user makes a request, it hits our infrastructure, we process it, and send a response. This is reliable, but it has limitations. Every request, good or bad, puts a load on our servers. Latency is dictated by the physical distance between the user and our data center. This is where edge computing, specifically with tools like Cloudflare Workers, changes the game.&lt;/p&gt;

&lt;p&gt;Workers are small, fast functions that run on Cloudflare's global network. They intercept HTTP requests &lt;em&gt;before&lt;/em&gt; they reach your origin server. This simple fact opens up a world of possibilities for building faster, more resilient, and more intelligent APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Path: A Quick Refresher
&lt;/h2&gt;

&lt;p&gt;Let's quickly visualize the traditional journey of an API request. A user's device sends a request. It travels across the internet to your data center, passes through a load balancer, hits one of your API servers, which then likely queries a database, and finally, the response travels all the way back.&lt;/p&gt;

&lt;p&gt;Every step in this chain adds latency. If your server is in Virginia and your user is in Tokyo, that's a long round trip. Furthermore, your server has to spend CPU cycles on every single request, whether it's for a simple data lookup or a malicious attempt to overload your service.&lt;/p&gt;

&lt;p&gt;This is what that flow looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18kia50icp8o6gvm6y4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18kia50icp8o6gvm6y4n.png" alt="A flowchart showing a traditional API request path: User sends a request to a Load Balancer, which forwards it to an API Server, which then queries a Database." width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This model has served us well, but it puts all the responsibility, and all the load, on your central infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Path: Intercepting Requests at the Edge
&lt;/h2&gt;

&lt;p&gt;Cloudflare Workers introduce a new step right at the beginning of this process. When a request is made to your domain, it first hits a Cloudflare data center close to the user. Your Worker code runs right there, in that data center.&lt;/p&gt;

&lt;p&gt;This Worker can now make decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Can I answer this request myself from a cache?&lt;/li&gt;
&lt;li&gt;  Is this request valid? Does it have the right authentication token?&lt;/li&gt;
&lt;li&gt;  Should I modify this request before sending it to the origin?&lt;/li&gt;
&lt;li&gt;  Should I route this request to a different origin server based on the user's location?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only if the Worker decides to, does the request continue on to your origin server. This means you can handle many requests without ever touching your own infrastructure, saving you money and reducing load.&lt;/p&gt;

&lt;p&gt;Here is the updated flow with a Worker:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eursb6v816mpkm0fqzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eursb6v816mpkm0fqzl.png" alt="A flowchart showing a modern API request path with an edge worker. A user request hits the worker first. If it is a cache hit, the worker responds directly. If it is a cache miss, the worker forwards the request to the origin server and database." width="800" height="914"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the Worker can serve responses directly from the edge (a cache hit), providing a massive speed boost. The origin server becomes the source of truth, not the first line of defense for every single request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four Practical Ways to Boost Your API with Workers
&lt;/h2&gt;

&lt;p&gt;Theory is great, but let's look at some real-world code examples. Workers are written in JavaScript or any language that compiles to WebAssembly, making them very accessible.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Supercharge Caching Beyond Simple Headers
&lt;/h3&gt;

&lt;p&gt;Standard HTTP caching with &lt;code&gt;Cache-Control&lt;/code&gt; headers is powerful but often blunt. What if you want to cache responses for anonymous users but always get fresh data for logged-in users? A Worker makes this simple.&lt;/p&gt;

&lt;p&gt;You can inspect the request for an authentication cookie or header and decide whether to serve a cached response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A simple Worker that caches based on user role&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache HIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache MISS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Check for an auth cookie. If it doesn't exist, the user is anonymous.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasAuthCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth_token=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch from the origin server&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Only cache responses for anonymous users and if the response was successful.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasAuthCookie&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;originResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheableResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;// Cache for 10 minutes&lt;/span&gt;
      &lt;span class="nx"&gt;cacheableResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cacheableResponse&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;originResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; Great for public-facing content on an API that also serves authenticated users. Think blog posts, product listings, or public profiles.&lt;br&gt;
&lt;strong&gt;When not to use this:&lt;/strong&gt; Avoid this for highly personalized or sensitive data that should never be cached, even for a short time.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Reject Bad Requests Before They Cost You
&lt;/h3&gt;

&lt;p&gt;Validating requests is critical. But why make your origin server do the work of decoding a JWT or checking a request body schema if the request is invalid anyway? You can do this at the edge and reject bad traffic immediately.&lt;/p&gt;

&lt;p&gt;Here is a simple example of validating a JWT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A Worker that validates a bearer token&lt;/span&gt;

&lt;span class="c1"&gt;// In a real app, you would use a proper library like 'jose' for JWT validation.&lt;/span&gt;
&lt;span class="c1"&gt;// This is a simplified example for demonstration.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isValidJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Dummy validation logic: in reality, you'd verify the signature&lt;/span&gt;
  &lt;span class="c1"&gt;// against a public key fetched from your auth provider.&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isExpired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedPayload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;isValidJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// If token is valid, proceed to the origin&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; Perfect for protecting authenticated API endpoints. It acts as a global authentication gateway, ensuring that your origin only receives requests from legitimate users.&lt;br&gt;
&lt;strong&gt;When not to use this:&lt;/strong&gt; For public endpoints that do not require authentication.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Run A/B Tests Without Touching Your API Code
&lt;/h3&gt;

&lt;p&gt;Want to test a new recommendation algorithm? Or a different response structure? You can use a Worker to route a percentage of users to a new version of your API (&lt;code&gt;v2&lt;/code&gt;) while the rest continue to use the stable version (&lt;code&gt;v1&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The Worker can check for a cookie or randomly assign users to a group, then silently rewrite the URL before sending it to your origin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A Worker for A/B testing&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(^| )&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=([^;]+)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ab-test-group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// If user is not in a group, assign them to one (50/50 split)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;treatment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// If user is in the 'treatment' group, rewrite the path to the v2 API&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;treatment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v2/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newRequest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a new response to add the cookie&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;newResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Set-Cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`ab-test-group=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; path=/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; Excellent for gradual rollouts and testing changes in production with minimal risk. Your backend team can deploy &lt;code&gt;v2&lt;/code&gt; endpoints, and the product team can control the traffic split without needing another deployment.&lt;br&gt;
&lt;strong&gt;When not to use this:&lt;/strong&gt; If the changes between &lt;code&gt;v1&lt;/code&gt; and &lt;code&gt;v2&lt;/code&gt; are so significant that they require different client-side handling. This pattern is best for functionally equivalent but internally different API versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Route Users to Their Nearest Data
&lt;/h3&gt;

&lt;p&gt;For global applications, data locality is key to low latency. If you have database replicas in the US, Europe, and Asia, you want users to hit the one closest to them. A Worker can determine the user's location from the request properties and route the request to the appropriate regional origin server.&lt;/p&gt;

&lt;p&gt;Cloudflare provides the &lt;code&gt;request.cf&lt;/code&gt; object, which contains geographic data. You can use &lt;code&gt;request.cf.continent&lt;/code&gt; or &lt;code&gt;request.cf.country&lt;/code&gt; to make routing decisions.&lt;/p&gt;

&lt;p&gt;This is a more advanced pattern that requires a multi-region backend setup, but it shows the power of running logic at the edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs: When the Edge Isn't the Right Place
&lt;/h2&gt;

&lt;p&gt;Workers are incredible, but they are not a replacement for your origin server. They are a complement. Here are some limitations to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Execution Limits:&lt;/strong&gt; Workers have limits on CPU time (typically 10-50ms) and memory. They are designed for short-lived tasks, not for heavy, long-running computations. For those, your origin server is still the right place.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Statelessness:&lt;/strong&gt; By default, Workers are stateless. You can't store data in memory between requests. To manage state, you need to use a service like Cloudflare KV (key-value store) or D1 (SQLite database), which adds complexity and its own performance considerations.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cold Starts:&lt;/strong&gt; While very fast (typically under 5ms), there can be a small 'cold start' penalty when a Worker is invoked for the first time in a specific location. For most APIs, this is negligible, but for ultra-low-latency applications, it's something to be aware of.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Local Development and Debugging:&lt;/strong&gt; The developer experience has improved massively with tools like Wrangler, but debugging a distributed edge function can still be more complex than debugging a monolithic application running on your local machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices for Building with Workers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Keep them small and fast:&lt;/strong&gt; A Worker should do one thing well. Chain multiple Workers for complex logic if needed, but favor small, focused functions.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cache everything you can:&lt;/strong&gt; Use the Cache API aggressively. It is your most powerful tool for reducing origin load and improving performance.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Handle errors gracefully:&lt;/strong&gt; If your Worker fails, what happens? Ensure you have proper error handling. You can choose to pass the request through to the origin on failure or return a cached response if available.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Manage secrets securely:&lt;/strong&gt; Use encrypted environment variables for API keys, tokens, and other secrets. Never hardcode them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Your Origin's New Best Friend
&lt;/h2&gt;

&lt;p&gt;Moving logic to the edge with Cloudflare Workers isn't about getting rid of your origin server. It's about making your origin server's job easier. By handling caching, authentication, validation, and routing at the edge, you free up your origin to do what it does best: execute core business logic and manage your data.&lt;/p&gt;

&lt;p&gt;For developers looking to build high-performance, globally scalable APIs, edge computing is no longer a niche concept. It is a fundamental tool for creating a better user experience and a more efficient, resilient backend architecture.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://www.qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.qudratullah.net/blog/beyond-the-origin-how-cloudflare-workers-forge-high-performance-apis" rel="noopener noreferrer"&gt;www.qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudflareworkers</category>
      <category>api</category>
      <category>performance</category>
      <category>edgecomputing</category>
    </item>
    <item>
      <title>Code Isn't Enough: A Framework for Championing Your Engineering Projects</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Wed, 22 Apr 2026 10:49:35 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/code-isnt-enough-a-framework-for-championing-your-engineering-projects-3oj7</link>
      <guid>https://forem.com/qudratullahdev/code-isnt-enough-a-framework-for-championing-your-engineering-projects-3oj7</guid>
      <description>&lt;p&gt;As engineers, we are trained to solve complex technical problems. We can spend weeks designing a scalable system, optimizing a database query, or refactoring a legacy module. We find elegance in well-written code and robust architecture. But I have seen more technically sound projects fail for social reasons than for technical ones. They fail because the engineers behind them could not build a compelling case for why the work mattered.&lt;/p&gt;

&lt;p&gt;After few years of leading teams, I've learned a hard truth: the best code doesn't always win. The best-communicated idea does. When you propose a project, you are not just writing a technical design document. You are competing for a finite pool of resources: time, money, and people's attention. Your proposal for a database upgrade is up against the product manager’s new shiny feature and the marketing team’s A/B testing framework.&lt;/p&gt;

&lt;p&gt;To win, you need to step outside the IDE and learn to speak the language of the business. You need a framework to translate your technical vision into a value proposition that resonates with everyone, from your product manager to the CFO. This isn't about office politics. It's about effective leadership. This is the framework I use to get critical engineering work prioritized and celebrated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Lenses of Engineering Value
&lt;/h2&gt;

&lt;p&gt;Not all engineering work is created equal. The first step in building a case for your project is to understand what kind of value it delivers. Trying to sell a security upgrade with the same pitch you use for a new user-facing feature is a recipe for failure. I categorize all engineering initiatives through four distinct lenses. A single project might touch on several, but it usually has a primary one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felzstf7kxj87gtv3oqpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felzstf7kxj87gtv3oqpf.png" alt="A flowchart showing 'Engineering Work' at the center, with arrows pointing to four boxes: 'Product Enablement (New Features)', 'Risk Reduction (Stability, Security)', 'Efficiency Gain (Speed, Cost Savings)', and 'Strategic Positioning (Future-proofing)'." width="800" height="824"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lens 1: Product Enablement (The Feature Driver)
&lt;/h3&gt;

&lt;p&gt;This is the simplest category to understand and the easiest to get prioritized. This is work that directly unlocks new features or improves existing ones for the end-user. Examples include building a new API endpoint for the mobile app, adding a new field to a data model, or integrating a third-party service. When you are working on product enablement, your closest ally is the product manager. Your success is measured in feature velocity and user impact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lens 2: Risk Reduction (The Insurance Policy)
&lt;/h3&gt;

&lt;p&gt;This is work that prevents bad things from happening. It’s the digital equivalent of buying insurance. It doesn't generate new revenue, but it protects existing revenue. Examples include patching security vulnerabilities, upgrading out-of-support libraries, improving monitoring, or setting up better disaster recovery plans. This work is often invisible when done well, which makes it a hard sell. The key is to quantify the potential cost of inaction. A data breach isn't just a technical problem; it's a multi-million dollar liability and a brand disaster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lens 3: Efficiency Gain (The Force Multiplier)
&lt;/h3&gt;

&lt;p&gt;This work makes your team, or the entire organization, faster and more effective. It's about reducing costs, either in time or money. Examples include improving CI/CD pipeline speed, automating a manual deployment process, optimizing cloud infrastructure to lower the monthly bill, or building a self-service tool for the support team. The value here is measured in developer hours saved, reduced operational costs, or faster time-to-market for everyone. This is work that pays dividends over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lens 4: Strategic Positioning (The Long Game)
&lt;/h3&gt;

&lt;p&gt;This is the most abstract and often the most difficult work to champion. It doesn't deliver immediate user value, prevent an imminent disaster, or save money next month. Instead, it sets the company up for future success. This includes large-scale projects like migrating from a monolith to microservices, adopting a new core technology like Kubernetes, or rebuilding the data platform to support future machine learning capabilities. These projects are foundational. They are about unlocking capabilities the business doesn't even know it needs yet. Pitching this requires a strong narrative about the future of the company and the market.&lt;/p&gt;

&lt;p&gt;Before you write a single line of a proposal, decide which lens (or lenses) best represents your project. This choice will define your entire communication strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioritizing Beyond Urgent vs. Important
&lt;/h2&gt;

&lt;p&gt;Once you've identified the value, you need to place it on the roadmap. We've all seen prioritization frameworks, but many of them fall short for engineering because they miss a key dimension: effort. A task can be important and urgent, but if it requires six months of work from your entire team, it's a very different decision than a two-day fix.&lt;/p&gt;

&lt;p&gt;I use a simple mental model with three axes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Impact:&lt;/strong&gt; How significant is the outcome? Use the Four Lenses to define this. A project that enables a flagship feature has high impact. A project that saves $500 a month in cloud costs has lower impact.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Urgency:&lt;/strong&gt; How quickly will the consequences of inaction be felt? An unpatched critical security flaw is highly urgent. A plan to migrate off a legacy system that is still functional is important, but not urgent.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Effort:&lt;/strong&gt; How much time and how many people will this take? Be realistic. Use t-shirt sizes (S, M, L) or a rough estimate in weeks. This isn't about perfect estimation; it's about orders of magnitude.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your goal is not just to work on high-impact, high-urgency tasks. That leads to a culture of firefighting. The real art is in balancing the portfolio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Quick Wins:&lt;/strong&gt; Low-Effort, High-Impact. Do these immediately. They build momentum and trust.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Big Bets:&lt;/strong&gt; High-Effort, High-Impact. These are your strategic projects. You must break them down into smaller, deliverable milestones. Never propose a one-year black-box project.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Thankless Chores:&lt;/strong&gt; Low-Impact, High-Urgency. These are the necessary evils, like minor bug fixes or support requests. Automate or streamline them as much as possible.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Time Sinks:&lt;/strong&gt; High-Effort, Low-Impact. Question why these are on your radar at all. This is where pet projects and gold-plating often hide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By explicitly mapping your work against these three axes, you can have a much more objective conversation with product and management about what to do next, and what to consciously choose &lt;em&gt;not&lt;/em&gt; to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Communication Playbook: From Tech Jargon to Business Case
&lt;/h2&gt;

&lt;p&gt;Having a great idea and a solid prioritization plan is only half the battle. Now you need to sell it. This is where most engineers stumble. We are comfortable talking about implementation details but struggle to articulate the business case. Here’s a playbook for translating technical needs into a language everyone understands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule 1: Frame the Problem, Not Your Solution
&lt;/h3&gt;

&lt;p&gt;Never lead with your proposed solution. Nobody cares that you want to use Kafka. They care about the problem you are trying to solve. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Wrong:&lt;/strong&gt; "We need to implement a message queue using Kafka for our services."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Right:&lt;/strong&gt; "Our checkout, shipping, and notification services are tightly coupled. When the notification service has a problem, it can block customers from placing orders. This has caused two site-wide outages in the last quarter, impacting an estimated $50k in revenue."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By framing the problem and its business impact first, you create a shared understanding and a sense of urgency. The solution becomes a logical next step, not a technical indulgence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule 2: Quantify Everything
&lt;/h3&gt;

&lt;p&gt;Numbers are the universal language of business. Replace vague adjectives with concrete metrics. This transforms your argument from an opinion into a data-driven proposal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Vague:&lt;/strong&gt; "Our CI/CD pipeline is slow."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Concrete:&lt;/strong&gt; "Our average build and deploy time is 45 minutes. With 10 engineers deploying twice a day, we lose 15 hours of productive engineering time daily. Reducing this to 10 minutes would save us over 300 engineering hours per month, which is equivalent to having an extra full-time engineer."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rule 3: Tailor the Message to the Audience
&lt;/h3&gt;

&lt;p&gt;Different stakeholders care about different things. A one-size-fits-all pitch will fail. You need to connect your project's value to their specific goals, using the Four Lenses as your guide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ya2xfylq17ujl50jmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ya2xfylq17ujl50jmq.png" alt="A flowchart showing an 'Engineering Initiative' leading to a decision point, 'What is the core value?'. From there, arrows point to different communication strategies for different stakeholders, such as talking to PMs about features or to Finance about cost savings." width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;For Product Managers:&lt;/strong&gt; Focus on Product Enablement and Efficiency. Frame your project in terms of feature velocity. "This refactoring will allow us to build the new personalization features 50% faster and A/B test different algorithms independently."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;For Executives and Finance:&lt;/strong&gt; Focus on Risk Reduction and Cost Savings. Frame it in terms of money and risk. "This server upgrade will reduce our monthly AWS bill by 15% and eliminate a critical security vulnerability that could lead to regulatory fines."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;For Other Engineering Teams:&lt;/strong&gt; You can be more technical, but still focus on the shared pain points. "By creating a shared authentication service, your teams will no longer need to maintain separate, inconsistent login logic. This will reduce bugs and simplify onboarding for new services."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Real-World Example: The Monolith Migration
&lt;/h3&gt;

&lt;p&gt;Let's put it all together. Imagine you want to propose breaking down a large, monolithic application. This is a classic Strategic Positioning project, and it's notoriously difficult to get approved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Wrong Pitch:&lt;/strong&gt;&lt;br&gt;
"I think we should refactor our monolith into microservices. It's a more modern architecture, and companies like Netflix and Google use it. We can use Docker and Kubernetes."&lt;/p&gt;

&lt;p&gt;This pitch is all solution and no problem. It's full of technical jargon and relies on appealing to authority without context. It will likely be met with skepticism and questions about risk and cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Right Pitch (Using the Framework):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frame the Problem (with data):&lt;/strong&gt; "Over the last 18 months, our time-to-market for new features has increased by 200%, from an average of 2 weeks to 6 weeks. Our bug rate has also increased by 40% per release. This is because any small change requires a full system deployment, a process that takes 3 hours and has a 15% failure rate. This is directly impacting our ability to compete and is burning out our engineers."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Apply the Lenses to Build a Holistic Case:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;(Product Enablement):&lt;/strong&gt; "By separating the User Profile service first, the Growth team can iterate on their onboarding experiments independently, without being blocked by the release schedule for the entire application. We estimate this will allow them to run 4x more experiments per quarter."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(Risk Reduction):&lt;/strong&gt; "Our current architecture means a bug in the non-critical recommendations engine can bring down the entire checkout process. Isolating services reduces our blast radius and improves site stability."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(Efficiency Gain):&lt;/strong&gt; "Smaller, independent services will reduce our build times from 45 minutes to under 5 minutes. This will return thousands of hours of productivity to the engineering team annually."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(Strategic Positioning):&lt;/strong&gt; "A modular architecture is the foundation for our long-term vision of offering our core logic as an API for enterprise partners, opening up a potential new revenue stream."&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Propose a Phased, Low-Risk Plan:&lt;/strong&gt; "We are not proposing a 'big bang' rewrite. We will start with one low-risk, high-impact service: User Profiles. This project will take one team of 4 engineers 8 weeks. The outcome will be a stable, independently deployable service and a reusable blueprint for future migrations. We will measure success by tracking the deployment frequency and bug rate for the Growth team."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pitch is a world apart. It's a business case, not a science project. It identifies a clear pain point, quantifies the impact, connects the solution to multiple sources of value, and presents a pragmatic, iterative plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Your Influence is Your Greatest Tool
&lt;/h2&gt;

&lt;p&gt;As you grow in your career, the scope of your impact will be defined less by the code you write and more by the change you can inspire. Your ability to see a problem, frame it in terms of business value, and build a coalition to solve it is what separates a senior engineer from a technical leader.&lt;/p&gt;

&lt;p&gt;This framework is not a magic formula, but it is a starting point. Start practicing it on smaller initiatives. When you see a manual process that could be automated, don't just complain about it. Quantify the time it wastes and pitch a solution. When you see a risky dependency, don't just note it down. Frame it as an insurance policy and explain the cost of a potential failure.&lt;/p&gt;

&lt;p&gt;By learning to look beyond the code and communicate the 'why' behind your work, you do more than just get your projects approved. You build trust, demonstrate leadership, and earn a seat at the table where the real strategic decisions are made.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://www.qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.qudratullah.net/blog/code-isnt-enough-a-framework-for-championing-your-engineering-projects" rel="noopener noreferrer"&gt;www.qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>engineeringleadership</category>
      <category>technicalstrategy</category>
      <category>communication</category>
      <category>prioritization</category>
    </item>
    <item>
      <title>Beyond console.log: A Guide to Production-Ready Logging in Node.js</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Fri, 17 Apr 2026 16:00:27 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/beyond-consolelog-a-guide-to-production-ready-logging-in-nodejs-4h5d</link>
      <guid>https://forem.com/qudratullahdev/beyond-consolelog-a-guide-to-production-ready-logging-in-nodejs-4h5d</guid>
      <description>&lt;p&gt;As developers, we all have a favorite debugging tool: &lt;code&gt;console.log&lt;/code&gt;. It is simple, it is fast, and it gets the job done when we are trying to figure out why a variable is &lt;code&gt;undefined&lt;/code&gt; on our local machine. But the habits we build in development can become liabilities in production. Relying on &lt;code&gt;console.log&lt;/code&gt; for a live application is like trying to find a specific grain of sand on a beach. It is inefficient, unstructured, and makes debugging real-world issues a nightmare.&lt;/p&gt;

&lt;p&gt;I have seen teams spend hours, sometimes days, sifting through messy, unsearchable log files, all because their logging strategy never matured beyond what they used for local development. Effective logging is not a feature you add at the end. It is a core part of a robust, maintainable, and observable system. Let’s explore how to level up from basic console statements to a professional logging setup that will save you time and headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;console.log&lt;/code&gt; Is Not Enough for Production
&lt;/h2&gt;

&lt;p&gt;When your application is running on a server, handling requests from thousands of users, &lt;code&gt;console.log('User created')&lt;/code&gt; just does not cut it. Here is why it falls short:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. No Structure
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;console.log&lt;/code&gt; statement outputs a simple string. While easy for a human to read one line at a time, it is very difficult for a machine to parse. Imagine you want to find all log entries for a specific user, or only show errors that happened after a certain time. With plain text logs, you are stuck using complex regular expressions. This is slow and error-prone.&lt;/p&gt;

&lt;p&gt;Production logs should be structured, typically as JSON. This allows you to easily filter, search, and aggregate logs in a dedicated logging tool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Before (console.log):&lt;/strong&gt; &lt;code&gt;User 123 failed to update profile.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;After (Structured Log):&lt;/strong&gt; &lt;code&gt;{"level":"error","time":1678886400000,"pid":456,"hostname":"server-1","userId":123,"msg":"Failed to update profile"}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. No Log Levels
&lt;/h3&gt;

&lt;p&gt;Not all log messages are equal. A message indicating the server has started is informational. A failed database connection is a critical error. &lt;code&gt;console.log&lt;/code&gt; has no concept of severity. While &lt;code&gt;console.warn&lt;/code&gt; and &lt;code&gt;console.error&lt;/code&gt; exist, they do not offer the granularity needed for a production system.&lt;/p&gt;

&lt;p&gt;Standard log levels include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;fatal&lt;/code&gt;&lt;/strong&gt;: The application is about to crash. A critical, service-ending event.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;error&lt;/code&gt;&lt;/strong&gt;: A serious error occurred, but the application can continue running (e.g., a failed API call to a third party).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;warn&lt;/code&gt;&lt;/strong&gt;: Something unexpected happened that is not an error but should be monitored (e.g., deprecated API usage).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;info&lt;/code&gt;&lt;/strong&gt;: Routine information about the application's operation (e.g., server started, user signed in).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;debug&lt;/code&gt;&lt;/strong&gt;: Detailed information useful only for debugging, typically turned off in production.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;trace&lt;/code&gt;&lt;/strong&gt;: Even more granular information, like detailed function call traces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using levels allows you to configure your logger to only output messages of a certain severity. In production, you might set the level to &lt;code&gt;info&lt;/code&gt;, while in development, you might set it to &lt;code&gt;debug&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Inflexible Output
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; always writes to the standard output (&lt;code&gt;stdout&lt;/code&gt;). In a production environment, you need more control. You might want to write logs to a file, send them to a third-party logging service like Datadog or Logstash, or even suppress them entirely during tests.&lt;br&gt;
A proper logging library allows you to configure different destinations, called "transports" or "streams".&lt;/p&gt;
&lt;h2&gt;
  
  
  The Pillars of Good Logging
&lt;/h2&gt;

&lt;p&gt;To build a production-ready logging system, we need to focus on a few key principles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Structured Data:&lt;/strong&gt; Always log in a machine-readable format like JSON.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Log Levels:&lt;/strong&gt; Use severity levels to categorize your logs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Context is King:&lt;/strong&gt; Every log entry should contain context to help you trace its origin. The most important piece of context is a unique request identifier.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Configurable Destinations:&lt;/strong&gt; Your application should not care where the logs go. The logging setup should handle routing them to the correct place based on the environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Choosing a Library: Pino for Performance
&lt;/h2&gt;

&lt;p&gt;While there are several great logging libraries for Node.js, such as Winston and Bunyan, my go-to choice for new projects is &lt;strong&gt;Pino&lt;/strong&gt;. It is incredibly fast and has very low overhead, which is important in a high-throughput Node.js application. It focuses on doing one thing well: emitting structured JSON logs.&lt;/p&gt;

&lt;p&gt;Let’s get started with a basic Pino setup.&lt;/p&gt;

&lt;p&gt;First, install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;pino
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create a simple logger instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// logger.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pino&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LOG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Default to 'info'&lt;/span&gt;
  &lt;span class="na"&gt;formatters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdTimeFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isoTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, we configure a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The log &lt;code&gt;level&lt;/code&gt; is set from an environment variable, falling back to &lt;code&gt;info&lt;/code&gt;. This is crucial for controlling log verbosity across different environments.&lt;/li&gt;
&lt;li&gt;  We use a &lt;code&gt;formatter&lt;/code&gt; to make the level label uppercase for consistency.&lt;/li&gt;
&lt;li&gt;  We set a standard ISO timestamp.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can use this logger anywhere in your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server is starting...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection is a bit slow.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to connect to Redis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Redis connection error.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we can pass an object as the first argument. Pino merges this object into the final JSON log line, which is the perfect way to add context.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Example: Logging in an Express.js App
&lt;/h2&gt;

&lt;p&gt;Let's integrate our logger into a simple Express server. The goal is to automatically log every incoming request and ensure all logs generated while handling that request are tied together with a unique ID.&lt;/p&gt;

&lt;p&gt;We will use &lt;code&gt;pino-http&lt;/code&gt;, a companion library for Pino.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express pino-http uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's set up our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pinoHttp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pino-http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add the pino-http middleware&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pinoHttp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// Define a custom request ID generator&lt;/span&gt;
  &lt;span class="na"&gt;genReqId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-request-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;existingId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Request-Id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Set it on the response header&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// pino-http adds the logger to the request object&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User accessed the home page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a simulated error!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occurred on the /error route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this server and hit the &lt;code&gt;/&lt;/code&gt; endpoint, you will see two log lines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; An &lt;code&gt;info&lt;/code&gt; log from our route handler.&lt;/li&gt;
&lt;li&gt; Another &lt;code&gt;info&lt;/code&gt; log that &lt;code&gt;pino-http&lt;/code&gt; automatically generates when the response is sent, including the status code and response time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both log lines will share the same &lt;code&gt;req.id&lt;/code&gt;, which is our unique request identifier. This is incredibly powerful. If a user reports an error, you can ask them for the &lt;code&gt;X-Request-Id&lt;/code&gt; from the response header and instantly find every single log associated with their request, even across multiple microservices if you pass the ID along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Logs in a Production Environment
&lt;/h2&gt;

&lt;p&gt;Generating logs is only half the battle. You also need a strategy for collecting, storing, and analyzing them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn6ythqs389kd6sknzq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn6ythqs389kd6sknzq5.png" alt="A diagram showing the flow of logs from a Node.js app, through a log agent, to a central logging service for analysis by a developer." width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This diagram shows a typical production logging pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Application (&lt;code&gt;Node.js App&lt;/code&gt;)&lt;/strong&gt;: Your application writes JSON logs to &lt;code&gt;stdout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Log Agent (&lt;code&gt;Log Agent on Server&lt;/code&gt;)&lt;/strong&gt;: A lightweight agent (like Fluentd or Vector) running on the same server collects these logs from &lt;code&gt;stdout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Central Logging Service&lt;/strong&gt;: The agent forwards the logs to a centralized system like Elasticsearch, Datadog, or Logz.io.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Storage and Analysis&lt;/strong&gt;: The service stores, indexes, and provides a user interface (like Kibana) for searching, visualizing, and creating alerts from the log data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach decouples your application from the logging backend. Your Node.js app's only job is to write structured logs to standard output. The rest is handled by the infrastructure, which is a key principle of the &lt;a href="https://12factor.net/logs" rel="noopener noreferrer"&gt;Twelve-Factor App&lt;/a&gt; methodology.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[Node.js App] -- JSON logs to stdout --&amp;gt; B(Log Agent on Server);
    B -- Ships logs --&amp;gt; C{Central Logging Service};
    C -- Stores &amp;amp; Indexes --&amp;gt; D[(Log Database)];
    E[Developer] -- Queries &amp;amp; Visualizes --&amp;gt; C;
    D -- Provides data --&amp;gt; C;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices and Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;Finally, here are some hard-won lessons from years of managing production systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;DO log in JSON.&lt;/strong&gt; I cannot stress this enough. It is the foundation of modern observability.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;DO include a request ID&lt;/strong&gt; in every log entry related to a request.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DON'T log sensitive information.&lt;/strong&gt; Never log passwords, API keys, or personally identifiable information (PII). Use Pino's redaction features to automatically strip sensitive fields from your log objects.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;redact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Qudrat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// The email and password will be replaced with '[REDACTED]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DO log errors with their stack traces.&lt;/strong&gt; The error message alone is often not enough. &lt;code&gt;logger.error({ err: myError }, 'A message')&lt;/code&gt; will automatically include the stack trace when you pass the error object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DON'T be too noisy.&lt;/strong&gt; Logging has a cost, both in performance and in storage. Use the &lt;code&gt;info&lt;/code&gt; level for significant events, not for every single function call. Save verbose logging for the &lt;code&gt;debug&lt;/code&gt; level, which you can enable on demand.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving beyond &lt;code&gt;console.log&lt;/code&gt; is a sign of a maturing developer. It shows you are thinking not just about making the code work, but about how it will be operated, monitored, and debugged in the real world. By embracing structured logging, you are building more resilient and maintainable applications, and your future self (and your team) will thank you for it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://qudratullah.net/blog/beyond-consolelog-a-guide-to-production-ready-logging-in-nodejs" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>logging</category>
      <category>observability</category>
      <category>pino</category>
    </item>
    <item>
      <title>From Vague to Valuable: A Practical Guide to Prompting LLMs - Generative AI</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Fri, 17 Apr 2026 15:39:20 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/from-vague-to-valuable-a-practical-guide-to-prompting-llms-generative-ai-56ab</link>
      <guid>https://forem.com/qudratullahdev/from-vague-to-valuable-a-practical-guide-to-prompting-llms-generative-ai-56ab</guid>
      <description>&lt;h2&gt;
  
  
  Your First Superpower in Tech
&lt;/h2&gt;

&lt;p&gt;Have you ever tried to get help from an AI chatbot, like ChatGPT, and received a completely useless answer? It feels frustrating. You know it's powerful, but it doesn't seem to understand you.&lt;/p&gt;

&lt;p&gt;Think of a Large Language Model (LLM) as a brilliant intern. They have read almost every book and website in existence. They are incredibly fast and knowledgeable. But they are also very literal. They have no real-world experience and will do &lt;em&gt;exactly&lt;/em&gt; what you ask, even if it's not what you &lt;em&gt;meant&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Your instruction to this intern is called a "prompt". Learning to write good prompts is like learning a new language for communicating with computers. It is one of the most valuable skills you can build today, whether you are a developer, a student, or just curious about technology. It's a true superpower.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core of a Good Prompt
&lt;/h2&gt;

&lt;p&gt;Getting a great result from an LLM isn't about secret tricks or magic words. It's about being clear and providing the right information. Let's break down the four key ingredients of a perfect prompt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevx43vcm8gb9oomkwxkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevx43vcm8gb9oomkwxkb.png" alt="A flowchart showing that a vague prompt leads to a poor result, while a good prompt, combining a specific task, context, persona, and format, leads to an excellent result." width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Be Specific and Clear
&lt;/h3&gt;

&lt;p&gt;Imagine walking into a coffee shop and saying, "Give me coffee." You might get a black filter coffee, an espresso, or a latte. It's a gamble. Instead, you say, "I'd like a large iced Americano with no sugar." Now you get exactly what you want.&lt;/p&gt;

&lt;p&gt;Prompts work the same way. Vague prompts lead to vague answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Bad Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write code for a button."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is like asking for "coffee". What language? What does it look like? What does it do?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Good Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write the HTML and CSS code for a clickable button. The button's text should be 'Download Report'. It should have a blue background (#3498db), white text, rounded corners (5px), and a light gray border. When a user hovers over it, the background should change to a darker blue (#2980b9)."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See the difference? We specified the language (HTML/CSS), the text, the colors, the shape, and the behavior. There is no room for guessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Provide Context
&lt;/h3&gt;

&lt;p&gt;The LLM does not know what you are working on or what you were thinking about five minutes ago. You have to provide all the necessary background information in your prompt.&lt;/p&gt;

&lt;p&gt;Think of it like asking a friend for directions. You wouldn't just ask, "How do I get to the library?" You'd say, "I'm currently at the corner of Main Street and Park Avenue. What's the quickest way to walk to the central library from here?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Bad Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can I make this function faster?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM has no idea what "this function" is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Good Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm working on a Python project. I have the following function that searches for a user in a list of a million user objects. It's very slow. How can I make it faster? Here is the code:&lt;/p&gt;


&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_user_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By providing the code and explaining the problem (it's slow with a large list), you give the LLM the context it needs to provide a helpful answer, like suggesting a dictionary for faster lookups.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Define the Persona and Format
&lt;/h3&gt;

&lt;p&gt;One of the most powerful features of LLMs is that you can tell them &lt;em&gt;who to be&lt;/em&gt; and &lt;em&gt;how to answer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persona:&lt;/strong&gt; Ask the LLM to adopt a role. This helps shape the tone and style of the response.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  "Act as a senior software engineer conducting a code review."&lt;/li&gt;
&lt;li&gt;  "You are a friendly tutor explaining a complex topic to a beginner."&lt;/li&gt;
&lt;li&gt;  "Pretend you are a skeptical project manager and point out potential flaws in my plan."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Format:&lt;/strong&gt; Tell the LLM exactly how you want the output structured.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  "Provide the answer as a JSON object."&lt;/li&gt;
&lt;li&gt;  "Explain the steps in a numbered list."&lt;/li&gt;
&lt;li&gt;  "Create a markdown table comparing these two databases."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A Good Prompt Combining Both:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Explain the concept of 'git merge' versus 'git rebase'. Act as a patient mentor talking to a junior developer. Use a simple analogy to explain each one. Finally, summarize the pros and cons of each in a markdown table."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This prompt tells the LLM its role (mentor), the target audience (junior developer), the content required (explanation, analogy), and the final output format (a markdown table). This level of detail almost guarantees a high-quality answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Iterate and Refine
&lt;/h3&gt;

&lt;p&gt;Your first prompt is often just the beginning of a conversation. Don't be discouraged if the first answer isn't perfect. Use it as a starting point and refine your request.&lt;/p&gt;

&lt;p&gt;Think of it like sculpting. You start with a block of clay and slowly shape it.&lt;/p&gt;

&lt;p&gt;A typical conversation might look like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; "Give me some ideas for a new mobile app."&lt;br&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; (Gives a generic list: a to-do list app, a fitness tracker, etc.)&lt;br&gt;
&lt;strong&gt;You:&lt;/strong&gt; "Those are okay, but I want something more unique. Focus on apps for people who enjoy gardening."&lt;br&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; (Gives better ideas: a plant identification app, a watering schedule tracker, a community for local gardeners.)&lt;br&gt;
&lt;strong&gt;You:&lt;/strong&gt; "I like the plant identification idea. Can you list the key features for an app like that? Present it as a bulleted list."&lt;/p&gt;

&lt;p&gt;Each follow-up prompt gets you closer to the perfect result.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Couple of Simple "Pro" Tricks
&lt;/h2&gt;

&lt;p&gt;Once you master the basics, you can try these slightly more advanced techniques.&lt;/p&gt;

&lt;h3&gt;
  
  
  Few-Shot Prompting: Show, Don't Just Tell
&lt;/h3&gt;

&lt;p&gt;Sometimes, the best way to get the format you want is to show the LLM a few examples. This is called "few-shot prompting".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need to extract the main subject from these sentences. Follow the pattern below.&lt;/p&gt;

&lt;p&gt;Sentence: The quick brown fox jumps over the lazy dog.&lt;br&gt;
Subject: fox&lt;/p&gt;

&lt;p&gt;Sentence: My team is shipping a new feature tomorrow.&lt;br&gt;
Subject: team&lt;/p&gt;

&lt;p&gt;Sentence: Artificial intelligence is transforming the world.&lt;br&gt;
Subject:"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By providing examples, you train the model on the exact output you expect. It will almost certainly answer "Artificial intelligence".&lt;/p&gt;

&lt;h3&gt;
  
  
  Chain-of-Thought: Ask it to Think Step-by-Step
&lt;/h3&gt;

&lt;p&gt;For problems that require logic or calculation, you can ask the LLM to explain its reasoning process. This often leads to more accurate results because it forces the model to break the problem down. Simply add "Let's think step-by-step" to your prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I have 50 apples. I give 10 to my friend, then I buy 25 more. I then split the new total evenly among myself and 4 friends. How many apples does each person get? Let's think step-by-step."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM will first calculate the intermediate steps before giving you the final answer, reducing the chance of a simple math error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Writing a good prompt is a skill, and like any skill, it gets better with practice. Don't worry about getting it perfect on the first try.&lt;/p&gt;

&lt;p&gt;Remember these key principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Be Specific:&lt;/strong&gt; Avoid vague requests. Detail exactly what you need.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Give Context:&lt;/strong&gt; Provide all the necessary background information. The LLM knows nothing but what you tell it.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Assign a Persona and Format:&lt;/strong&gt; Tell the AI who to be and how to structure its answer.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Iterate:&lt;/strong&gt; Treat it as a conversation. Refine your prompts based on the answers you get.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Show, Don't Tell:&lt;/strong&gt; Use examples (few-shot) to guide the AI's output for complex formatting.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Think Step-by-Step:&lt;/strong&gt; For logic problems, ask the AI to show its work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mastering this skill will not only help you get better answers from AI but also make you a clearer communicator and a more effective problem-solver. Now go ahead and give that brilliant intern some clear instructions!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://qudratullah.net/blog/from-vague-to-valuable-a-practical-guide-to-prompting-llms" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>promptengineering</category>
      <category>llm</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Vector Databases Made Simple: Your First Step Into Modern Data Storage</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Fri, 17 Apr 2026 13:24:31 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/vector-databases-made-simple-your-first-step-into-modern-data-storage-oo5</link>
      <guid>https://forem.com/qudratullahdev/vector-databases-made-simple-your-first-step-into-modern-data-storage-oo5</guid>
      <description>&lt;h2&gt;
  
  
  What Are Vector Databases?
&lt;/h2&gt;

&lt;p&gt;Imagine you're organizing your music collection. Instead of just sorting by artist name or genre, you could organize songs by how they "feel" - grouping similar vibes together. Vector databases work similarly, but with data.&lt;/p&gt;

&lt;p&gt;A vector database stores information as mathematical representations called vectors. Think of vectors as coordinates that describe the "essence" or "meaning" of your data. Just like GPS coordinates tell you where something is on Earth, vectors tell you where your data sits in a multi-dimensional space of meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care About Vector Databases?
&lt;/h2&gt;

&lt;p&gt;Traditional databases are great for exact matches. If you search for "John Smith," you get exactly "John Smith." But what if you want to find "similar" things? What if you want to search for "happy songs" or "articles about cooking pasta"?&lt;/p&gt;

&lt;p&gt;This is where vector databases shine. They excel at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Similarity search&lt;/strong&gt;: Finding items that are alike&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic search&lt;/strong&gt;: Understanding meaning, not just keywords&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI applications&lt;/strong&gt;: Powering chatbots, recommendation systems, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Do Vector Databases Work?
&lt;/h2&gt;

&lt;p&gt;Let me explain with a simple analogy. Imagine you're describing people using only three numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Height (in inches)&lt;/li&gt;
&lt;li&gt;Age (in years) &lt;/li&gt;
&lt;li&gt;Income (in thousands)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So John might be [70, 25, 45] and Sarah might be [68, 27, 50]. These numbers are vectors. To find people similar to John, you'd look for vectors with numbers close to his.&lt;/p&gt;

&lt;p&gt;In real vector databases, instead of 3 numbers, you might have 384 or 1,536 numbers describing the "meaning" of text, images, or other data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your First Vector Database Example
&lt;/h2&gt;

&lt;p&gt;Let's build a simple example using Python and a popular vector database called Chroma. Don't worry if you're new to Python - I'll explain each step.&lt;/p&gt;

&lt;p&gt;First, install the required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chromadb sentence-transformers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create a basic vector database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chromadb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;

&lt;span class="c1"&gt;# Create a vector database client
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chromadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Create a collection (like a table in traditional databases)
&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Some sample documents
&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I love pizza and pasta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The weather is sunny today&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python is a great programming language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I enjoy Italian food very much&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;It&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s raining outside right now&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Add documents to our collection
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Documents added to vector database!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's search for similar documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Search for documents similar to a query
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;query_texts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I like Italian cuisine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="c1"&gt;# Get top 2 similar results
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Similar documents:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;documents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this code, it will find documents about Italian food, even though your search didn't use the exact words "pizza" or "pasta."&lt;/p&gt;

&lt;h2&gt;
  
  
  Popular Vector Database Options
&lt;/h2&gt;

&lt;p&gt;As a beginner, here are the most beginner-friendly options:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Chroma&lt;/strong&gt; (Best for Learning)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy to set up&lt;/li&gt;
&lt;li&gt;Works locally on your computer&lt;/li&gt;
&lt;li&gt;Great documentation&lt;/li&gt;
&lt;li&gt;Free to use&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pinecone&lt;/strong&gt; (Best for Production)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cloud-based (no setup required)&lt;/li&gt;
&lt;li&gt;Very fast&lt;/li&gt;
&lt;li&gt;Has a free tier&lt;/li&gt;
&lt;li&gt;Great for real applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Weaviate&lt;/strong&gt; (Best for Advanced Features)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open source&lt;/li&gt;
&lt;li&gt;Lots of built-in features&lt;/li&gt;
&lt;li&gt;Can run locally or in the cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Use Cases You'll See
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Chatbots and Q&amp;amp;A Systems&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When you ask ChatGPT a question, it uses vector databases to find relevant information from its training data.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Recommendation Systems&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Netflix uses similar technology to recommend movies you might like based on what you've watched.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Image Search&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Google Photos can find "pictures of dogs" even if you never tagged them as dogs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Document Search&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Companies use vector databases to help employees find relevant documents, even with vague queries like "contract about software licensing."&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Start Small&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Begin with a few dozen documents. Don't try to build the next Google on day one.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use Pre-trained Models&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Don't create your own vectors from scratch. Use models like &lt;code&gt;sentence-transformers&lt;/code&gt; that are already trained.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Test Your Searches&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Try different queries and see what results you get. This helps you understand how your vector database "thinks."&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitor Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As your database grows, searches might get slower. Most vector databases have settings to help with this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Beginner Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1&lt;/strong&gt;: Using too many dimensions&lt;br&gt;
More dimensions aren't always better. Start with 384 or 768 dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2&lt;/strong&gt;: Not preprocessing your data&lt;br&gt;
Clean your text data first - remove extra spaces, fix typos, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3&lt;/strong&gt;: Expecting perfect results immediately&lt;br&gt;
Vector search is about similarity, not exact matches. Results improve as you fine-tune.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Once you're comfortable with basic vector databases, you can explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid search&lt;/strong&gt;: Combining vector search with traditional keyword search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning&lt;/strong&gt;: Customizing models for your specific data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production deployment&lt;/strong&gt;: Moving from local testing to real applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vector databases store data as mathematical representations that capture meaning&lt;/li&gt;
&lt;li&gt;They excel at finding similar items, not just exact matches&lt;/li&gt;
&lt;li&gt;Start with simple tools like Chroma for learning&lt;/li&gt;
&lt;li&gt;Use pre-trained models instead of building from scratch&lt;/li&gt;
&lt;li&gt;Common applications include chatbots, recommendations, and semantic search&lt;/li&gt;
&lt;li&gt;Begin with small datasets and gradually scale up&lt;/li&gt;
&lt;li&gt;Vector databases are becoming essential for modern AI applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vector databases might seem complex at first, but they're just tools for finding similar things. Start with the simple example above, experiment with different queries, and you'll quickly see their power. The future of search and AI heavily relies on this technology, so learning it now puts you ahead of the curve.&lt;/p&gt;

&lt;p&gt;If you want me to go ahead with this topic, feel free to tell me. I'll go deeper with real-world working application demos and how to do that step by step.&lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://qudratullah.net/blog/vector-databases-made-simple-your-first-step-into-modern-data-storage" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vectordatabases</category>
      <category>beginners</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Build Your First AI Search in 30 Minutes: A Complete RAG Tutorial</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Thu, 16 Apr 2026 20:15:29 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/build-your-first-ai-search-in-30-minutes-a-complete-rag-tutorial-28d9</link>
      <guid>https://forem.com/qudratullahdev/build-your-first-ai-search-in-30-minutes-a-complete-rag-tutorial-28d9</guid>
      <description>&lt;h2&gt;
  
  
  What is RAG and Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;RAG stands for Retrieval-Augmented Generation. Think of it like having a super-smart assistant who can quickly search through your documents and then give you intelligent answers based on what they found.&lt;/p&gt;

&lt;p&gt;Imagine you have thousands of company documents, and instead of spending hours searching through them manually, you could just ask questions like "What's our vacation policy?" and get instant, accurate answers. That's exactly what RAG does.&lt;/p&gt;

&lt;p&gt;The "Retrieval" part finds relevant information, and the "Generation" part creates human-like responses using that information. It's like combining Google search with ChatGPT, but for your own data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How RAG Works: The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff51y2npi9rjtdmm4h8p9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff51y2npi9rjtdmm4h8p9.jpg" alt="RAG workflow diagram showing three steps" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RAG works in three simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store&lt;/strong&gt;: Break your documents into small chunks and convert them into numbers (called embeddings) that computers understand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: When you ask a question, find the most relevant chunks from your stored data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt;: Feed those relevant chunks to an AI model (like GPT) to create a natural answer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7bsx71m77eu9u1v35dt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7bsx71m77eu9u1v35dt.png" alt="What are embeddings" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of it like organizing a massive library. Instead of browsing every book, you have a librarian who knows exactly where to find information and can summarize it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your First RAG System
&lt;/h2&gt;

&lt;p&gt;Let's build a simple RAG system that can answer questions about your documents. We'll use Python and some helpful libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Required Libraries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain openai chromadb sentence-transformers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Prepare Your Documents
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# documents.py
&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Our company offers 20 days of paid vacation per year. Employees can carry over up to 5 unused days to the next year.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The office hours are 9 AM to 6 PM, Monday through Friday. Remote work is allowed up to 3 days per week.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Health insurance covers 100% of employee premiums and 80% of family member premiums.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The annual performance review happens in December. Salary increases are effective from January.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Create the RAG System
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht1jrsdwmq9e1wilscfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht1jrsdwmq9e1wilscfc.png" alt="RAG System Steps" width="800" height="458"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rag_system.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CharacterTextSplitter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Chroma&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Set your OpenAI API key
&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-api-key-here&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleRAG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Split documents into smaller chunks
&lt;/span&gt;        &lt;span class="n"&gt;text_splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create embeddings (convert text to numbers)
&lt;/span&gt;        &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HuggingFaceEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Store in vector database
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vectorstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Chroma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Set up the question-answering chain
&lt;/span&gt;        &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qa_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_chain_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;chain_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stuff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vectorstore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qa_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;

&lt;span class="n"&gt;rag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleRAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ask_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How many vacation days do I get?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Test Your RAG System
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test_rag.py
&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How many vacation days do I get?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can I work from home?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;When do performance reviews happen?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What does health insurance cover?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Q: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ask_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making It Better: Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Chunk Your Data Smartly
&lt;/h3&gt;

&lt;p&gt;Don't just split text randomly. Split by paragraphs, sentences, or logical sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Better text splitting
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;

&lt;span class="n"&gt;text_splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;separators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Use Better Embeddings
&lt;/h3&gt;

&lt;p&gt;For better search results, use more powerful embedding models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;

&lt;span class="c1"&gt;# More accurate but costs money
&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Free alternative that works well
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;
&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HuggingFaceEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentence-transformers/all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Add Memory for Conversations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.memory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConversationBufferMemory&lt;/span&gt;

&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConversationBufferMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;qa_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_chain_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vectorstore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Using chunks that are too big or too small&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too big: AI gets confused with too much information&lt;/li&gt;
&lt;li&gt;Too small: Important context gets lost&lt;/li&gt;
&lt;li&gt;Sweet spot: 500-1500 characters per chunk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Not handling edge cases&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;safe_ask_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please ask a valid question.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qa_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sorry, I couldn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t process your question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mistake 3: Ignoring document quality&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean your documents first&lt;/li&gt;
&lt;li&gt;Remove unnecessary formatting&lt;/li&gt;
&lt;li&gt;Fix typos and inconsistencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scaling Up: Next Steps
&lt;/h2&gt;

&lt;p&gt;Once you have a basic RAG system working, you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add more document types&lt;/strong&gt;: PDFs, Word docs, web pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve the UI&lt;/strong&gt;: Build a web interface with Streamlit or Flask&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use better databases&lt;/strong&gt;: PostgreSQL with pgvector for production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add authentication&lt;/strong&gt;: Secure your system for multiple users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor performance&lt;/strong&gt;: Track which questions work well and which don't&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RAG combines search and AI generation to answer questions about your documents&lt;/li&gt;
&lt;li&gt;You need three main components: document storage, similarity search, and text generation&lt;/li&gt;
&lt;li&gt;Start simple with basic libraries, then improve gradually&lt;/li&gt;
&lt;li&gt;Good document chunking is crucial for accurate results&lt;/li&gt;
&lt;li&gt;Test with real questions your users would ask&lt;/li&gt;
&lt;li&gt;Always handle errors gracefully in production systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RAG isn't magic, but it's incredibly powerful when done right. Start with this simple example, experiment with your own documents, and gradually add more features. Before you know it, you'll have built an AI assistant that actually knows about your specific domain.&lt;/p&gt;

&lt;p&gt;The best part? This is just the beginning. RAG technology is evolving rapidly, and mastering the basics now will set you up for even more exciting developments ahead.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Qudrat Ullah&lt;/strong&gt;, an Engineering Lead with 10+ years building scalable systems across fintech, media, and enterprise. I write about Node.js, cloud infrastructure, AI, and engineering leadership.&lt;/p&gt;

&lt;p&gt;Find me online: &lt;a href="https://www.linkedin.com/in/qudratullah-me/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://qudratullah.net" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, share it with a fellow engineer or drop your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://qudratullah.net/blog/build-your-first-ai-search-in-30-minutes-a-complete-rag-tutorial" rel="noopener noreferrer"&gt;qudratullah.net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Generative AI vs Agentic AI</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Sun, 25 Jan 2026 15:32:52 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/generative-ai-vs-agentic-ai-52e9</link>
      <guid>https://forem.com/qudratullahdev/generative-ai-vs-agentic-ai-52e9</guid>
      <description>&lt;h1&gt;
  
  
  Generative AI vs Agentic AI
&lt;/h1&gt;

&lt;p&gt;Artificial Intelligence is moving fast, and two terms are showing up everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generative AI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic AI&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They sound similar, but they serve &lt;strong&gt;very different purposes&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article explains the difference in a simple, practical way &lt;br&gt;
no buzzwords, no heavy theory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🤖 What Is Generative AI?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Generative AI creates content.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You give it a prompt, and it generates an output such as text, images, or code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of Generative AI as a very smart content creator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It responds to your request, and then it stops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ChatGPT writing text&lt;/li&gt;
&lt;li&gt;DALL·E generating images&lt;/li&gt;
&lt;li&gt;GitHub Copilot suggesting code&lt;/li&gt;
&lt;li&gt;AI summarising documents&lt;/li&gt;
&lt;li&gt;AI translating languages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple example
&lt;/h3&gt;

&lt;p&gt;You ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write a job description for a frontend developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Generative AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates the text
&lt;/li&gt;
&lt;li&gt;Returns the response
&lt;/li&gt;
&lt;li&gt;Ends the interaction
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Task completed&lt;br&gt;&lt;br&gt;
❌ No follow-up&lt;br&gt;&lt;br&gt;
❌ No action taken  &lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ What Is Agentic AI?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI doesn’t just generate responses, it takes action.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of only answering, it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand a goal&lt;/li&gt;
&lt;li&gt;Break it into steps&lt;/li&gt;
&lt;li&gt;Use tools&lt;/li&gt;
&lt;li&gt;Make decisions&lt;/li&gt;
&lt;li&gt;Execute tasks&lt;/li&gt;
&lt;li&gt;Repeat until the goal is achieved&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of Agentic AI as a digital worker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Simple example
&lt;/h3&gt;

&lt;p&gt;You say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find family-friendly events in London this weekend and email me the best five.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An Agentic AI might:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search multiple event websites
&lt;/li&gt;
&lt;li&gt;Open and analyse pages
&lt;/li&gt;
&lt;li&gt;Extract dates, prices, and locations
&lt;/li&gt;
&lt;li&gt;Filter family-friendly events
&lt;/li&gt;
&lt;li&gt;Rank the best options
&lt;/li&gt;
&lt;li&gt;Send you an email
&lt;/li&gt;
&lt;li&gt;Store results for future use
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not just AI responding — this is &lt;strong&gt;AI working toward a goal&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 The Key Difference
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Generative AI answers questions.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Agentic AI completes goals.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧩 Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Generative AI&lt;/th&gt;
&lt;th&gt;Agentic AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generates text or images&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Makes decisions&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses tools or APIs&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executes actions&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works autonomously&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Has goals&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🧠 Everyday Analogy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Generative AI&lt;/strong&gt; is like a skilled writer.&lt;br&gt;&lt;br&gt;
You ask a question, they write an answer, and the task ends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI&lt;/strong&gt; is like a personal assistant.&lt;br&gt;&lt;br&gt;
You give a goal, they figure out how to achieve it, and they get it done.&lt;/p&gt;

&lt;h2&gt;
  
  
  👨‍💻 Why This Matters for Developers
&lt;/h2&gt;

&lt;p&gt;If you’re building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chatbots or writing tools → &lt;strong&gt;Generative AI&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Content generation platforms → &lt;strong&gt;Generative AI&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Automation systems → &lt;strong&gt;Agentic AI&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AI assistants or workers → &lt;strong&gt;Agentic AI&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;End-to-end workflows → &lt;strong&gt;Agentic AI&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most real-world business value is shifting toward &lt;strong&gt;agentic systems&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Final Takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generative AI creates content
&lt;/li&gt;
&lt;li&gt;Agentic AI achieves goals
&lt;/li&gt;
&lt;li&gt;One responds
&lt;/li&gt;
&lt;li&gt;The other acts
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If Generative AI is the brain,&lt;br&gt;
Agentic AI is the brain &lt;strong&gt;plus hands&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧠 One Last Question for You
&lt;/h2&gt;

&lt;p&gt;We’ve moved very quickly from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI that generates answers&lt;br&gt;
to&lt;br&gt;
AI that takes action&lt;br&gt;
The real question now is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should AI stop acting on our behalf?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Should AI agents be fully autonomous?&lt;/li&gt;
&lt;li&gt;Or should humans always stay in the loop?&lt;/li&gt;
&lt;li&gt;Where do &lt;em&gt;you&lt;/em&gt; draw the line between assistant and decision-maker?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👇&lt;br&gt;&lt;br&gt;
&lt;strong&gt;I’d love to hear your thoughts in the comments.&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Fix Chromium on Vercel: A Complete Guide to Solving the “libnspr4.so” Error</title>
      <dc:creator>qudrat ullah</dc:creator>
      <pubDate>Fri, 23 Jan 2026 14:26:47 +0000</pubDate>
      <link>https://forem.com/qudratullahdev/how-to-fix-chromium-on-vercel-a-complete-guide-to-solving-the-libnspr4so-error-4i9j</link>
      <guid>https://forem.com/qudratullahdev/how-to-fix-chromium-on-vercel-a-complete-guide-to-solving-the-libnspr4so-error-4i9j</guid>
      <description>&lt;p&gt;If you're deploying a Next.js app with Playwright and Chromium to Vercel, you've probably hit this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/tmp/chromium: error &lt;span class="k"&gt;while &lt;/span&gt;loading shared libraries: libnspr4.so: cannot open shared object file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works perfectly on your local machine but fails on Vercel. This is one of the most frustrating deployment issues developers face when working with headless browsers in serverless environments.&lt;/p&gt;

&lt;p&gt;After analyzing 50+ GitHub issues and testing multiple solutions, I discovered the root cause and the complete fix. Here's everything you need to know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Works Locally But Fails on Vercel
&lt;/h2&gt;

&lt;p&gt;The fundamental difference between your local environment and Vercel's serverless environment causes this issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On your local machine:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System libraries like &lt;code&gt;libnspr4.so&lt;/code&gt; and &lt;code&gt;libnss3.so&lt;/code&gt; are pre-installed&lt;/li&gt;
&lt;li&gt;You have full file system access&lt;/li&gt;
&lt;li&gt;Complete Node.js environment with all dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;On Vercel serverless:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimal Linux container with no system libraries&lt;/li&gt;
&lt;li&gt;Read-only file system except for the &lt;code&gt;/tmp&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Environment variables must exist before modules load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The critical issue:&lt;/strong&gt; The sparticuz/chromium package checks for &lt;code&gt;AWS_LAMBDA_JS_RUNTIME&lt;/code&gt; when the module imports, but if you set it in your code, it happens after the module is already loaded. This timing mismatch breaks everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Five Problems and Their Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem 1: Wrong Package Choice
&lt;/h3&gt;

&lt;p&gt;Many developers try to use the full Playwright package, which doesn't work on Vercel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't use:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"playwright"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.39.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package exceeds Vercel's 50MB limit and tries to download the browser at runtime, which fails in serverless environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use instead:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"playwright-core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.39.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@sparticuz/chromium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^131.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; &lt;code&gt;playwright-core&lt;/code&gt; is only about 5MB, and the sparticuz chromium package is about 40MB. Together they stay under Vercel's 50MB limit, and the chromium package provides a pre-bundled Chromium optimized for serverless environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: Environment Variable Timing
&lt;/h3&gt;

&lt;p&gt;This is where most developers get stuck. The timing of when environment variables are set matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you import the package, it immediately checks for &lt;code&gt;AWS_LAMBDA_JS_RUNTIME&lt;/code&gt;. But if you set it in your code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_LAMBDA_JS_RUNTIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs22.x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module has already loaded and checked for the variable. It's too late.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set the environment variable in the Vercel Dashboard, not just in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's how:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Vercel Dashboard&lt;/li&gt;
&lt;li&gt;Select your project&lt;/li&gt;
&lt;li&gt;Go to Settings → Environment Variables&lt;/li&gt;
&lt;li&gt;Add a new variable: &lt;code&gt;AWS_LAMBDA_JS_RUNTIME&lt;/code&gt; with value &lt;code&gt;nodejs22.x&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Apply to Production, Preview, and Development environments&lt;/li&gt;
&lt;li&gt;Redeploy your application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why the Dashboard matters:&lt;/strong&gt; Environment variables set in the Vercel Dashboard are available before any modules load, which is exactly when the chromium package needs them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 3: Libraries Extracted But Not Found (The Critical Fix)
&lt;/h3&gt;

&lt;p&gt;This was the breakthrough that solved the issue. Even when libraries are extracted correctly, Chromium can't find them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Libraries are extracted to &lt;code&gt;/tmp/libnspr4.so&lt;/code&gt; and &lt;code&gt;/tmp/libnss3.so&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Chromium executable is in &lt;code&gt;/tmp/chromium&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;But Chromium can't find the libraries because the system doesn't know where to look&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; to the executable directory before launching the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;executablePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;execDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// CRITICAL: Set LD_LIBRARY_PATH so Chromium can find libraries&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LD_LIBRARY_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;execDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; tells the Linux system loader where to find shared libraries. By setting it to the same directory where the Chromium executable and libraries are located, Chromium can successfully load the required libraries.&lt;/p&gt;

&lt;p&gt;This was the missing piece that most solutions don't mention. Without this, even with the correct environment variable, Chromium will fail to find the libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 4: Old Package Version
&lt;/h3&gt;

&lt;p&gt;Using an outdated version of the chromium package can cause compatibility issues with Node.js 22.x.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't use:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"@sparticuz/chromium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"119.0.2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"@sparticuz/chromium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^131.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Version 131.0.0 has better support for Node.js 22.x and includes fixes for common serverless deployment issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 5: Browser Freezing
&lt;/h3&gt;

&lt;p&gt;Some developers report that the browser freezes after creating a new page, even when everything else is configured correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Browser freezes after "Creating new page" message appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Disable graphics mode before launching the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setGraphicsMode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setGraphicsMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Serverless environments don't have GPU support. Disabling graphics mode prevents the browser from trying to use GPU features that aren't available, which causes freezing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Working Solution
&lt;/h2&gt;

&lt;p&gt;Here's the complete implementation that works on Vercel:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Package.json Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@sparticuz/chromium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^131.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"playwright-core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.39.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"22.x"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Next.js Configuration
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;next.config.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;serverExternalPackages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents Next.js from bundling these packages, which is essential for them to work correctly in the serverless environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Browser Launch Code
&lt;/h3&gt;

&lt;p&gt;Create a file &lt;code&gt;lib/browser.ts&lt;/code&gt; with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright-core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browserInstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBrowser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browserInstance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;browserInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isVercel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERCEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERCEL_ENV&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isVercel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set runtime (fallback if not in Dashboard)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_LAMBDA_JS_RUNTIME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_LAMBDA_JS_RUNTIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs22.x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Set graphics mode to prevent freezing&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setGraphicsMode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setGraphicsMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Get executable and set library path&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;executablePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;execDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// CRITICAL: Set LD_LIBRARY_PATH&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LD_LIBRARY_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;execDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;browserInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromiumPack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Local development&lt;/span&gt;
    &lt;span class="nx"&gt;browserInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--no-sandbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;browserInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. API Route Configuration
&lt;/h3&gt;

&lt;p&gt;In your API route file (e.g., &lt;code&gt;app/api/screenshot/route.ts&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getBrowser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxDuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBrowser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Your screenshot code here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;runtime = "nodejs"&lt;/code&gt; ensures the function runs in the Node.js runtime, and &lt;code&gt;maxDuration = 300&lt;/code&gt; gives you 5 minutes for screenshot operations (requires Vercel Pro plan).&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Vercel Dashboard Settings
&lt;/h3&gt;

&lt;p&gt;Before deploying, configure these settings in your Vercel Dashboard:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variable:&lt;/strong&gt; Add &lt;code&gt;AWS_LAMBDA_JS_RUNTIME&lt;/code&gt; with value &lt;code&gt;nodejs22.x&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disable Fluid Compute:&lt;/strong&gt; Go to Settings → Functions → Fluid Compute → Turn OFF&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro Plan:&lt;/strong&gt; Required for 300-second timeouts (Hobby plan only allows 10 seconds)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Three Critical Fixes
&lt;/h2&gt;

&lt;p&gt;You need all three of these fixes for Chromium to work on Vercel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Correct packages:&lt;/strong&gt; Use &lt;code&gt;playwright-core&lt;/code&gt; and sparticuz chromium version 131.0.0 or higher&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variable:&lt;/strong&gt; Set &lt;code&gt;AWS_LAMBDA_JS_RUNTIME=nodejs22.x&lt;/code&gt; in Vercel Dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library path:&lt;/strong&gt; Set &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; to the executable directory in your code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Missing any one of these will cause the deployment to fail. They work together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The environment variable tells the package which runtime to use&lt;/li&gt;
&lt;li&gt;The library path tells Chromium where to find the extracted libraries&lt;/li&gt;
&lt;li&gt;The correct packages ensure everything fits within Vercel's limits&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Troubleshooting Checklist
&lt;/h2&gt;

&lt;p&gt;If you're still experiencing issues, check each of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;AWS_LAMBDA_JS_RUNTIME=nodejs22.x&lt;/code&gt; is set in Vercel Dashboard (not just in code)&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; is set in code before browser launch&lt;/li&gt;
&lt;li&gt;[ ] Using &lt;code&gt;playwright-core&lt;/code&gt; (not the full playwright package)&lt;/li&gt;
&lt;li&gt;[ ] Using sparticuz chromium version 131.0.0 or higher&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;setGraphicsMode(false)&lt;/code&gt; is called before launching the browser&lt;/li&gt;
&lt;li&gt;[ ] Fluid Compute is disabled in Vercel settings&lt;/li&gt;
&lt;li&gt;[ ] You're on Vercel Pro plan for extended timeouts&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;serverExternalPackages&lt;/code&gt; is configured in &lt;code&gt;next.config.ts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Solution Works
&lt;/h2&gt;

&lt;p&gt;This solution addresses all the fundamental differences between local and serverless environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Package choice:&lt;/strong&gt; &lt;code&gt;playwright-core&lt;/code&gt; and the chromium package are designed for serverless and stay within size limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variable:&lt;/strong&gt; Setting it in the Dashboard ensures it's available when modules load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library path:&lt;/strong&gt; &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; tells the system where to find shared libraries in the minimal serverless environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The critical insight:&lt;/strong&gt; Both the environment variable (for runtime detection) and the library path (for finding libraries) are required. Most solutions only mention one or the other, but you need both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying Chromium on Vercel requires three essential components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The right packages (&lt;code&gt;playwright-core&lt;/code&gt; and the chromium package)&lt;/li&gt;
&lt;li&gt;Environment variable configured in Vercel Dashboard&lt;/li&gt;
&lt;li&gt;Library path set in your code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have all three configured correctly, your screenshot application will work reliably on Vercel's serverless infrastructure. The key is understanding that serverless environments are fundamentally different from local development, and each difference requires a specific solution.&lt;/p&gt;

&lt;p&gt;This solution was discovered after analyzing 50+ GitHub issues and testing multiple approaches. The breakthrough was realizing that &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; must be set to the executable directory, which most documentation doesn't mention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Sparticuz/chromium" rel="noopener noreferrer"&gt;sparticuz/chromium GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.vercel.com/t/issue-with-puppeteer-and-chrome-aws-lambda-on-node-js-18-in-vercel/4229" rel="noopener noreferrer"&gt;Vercel Community Discussion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you found this helpful, feel free to share your experience or ask questions in the comments below!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>serverless</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
