<?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>DEV Community: Ugochukwu Nebolisa</title>
    <description>The latest articles on DEV Community by Ugochukwu Nebolisa (@ugochukwu_nebolisa_99efd3).</description>
    <link>https://dev.to/ugochukwu_nebolisa_99efd3</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%2F1699622%2F9ddb95a9-5cb8-45e7-bae7-085847295243.png</url>
      <title>DEV Community: Ugochukwu Nebolisa</title>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ugochukwu_nebolisa_99efd3"/>
    <language>en</language>
    <item>
      <title>HTTP Caching Headers: The Performance Optimization You're Probably Missing</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Tue, 17 Mar 2026 22:59:36 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/http-caching-headers-the-performance-optimization-youre-probably-missing-38p</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/http-caching-headers-the-performance-optimization-youre-probably-missing-38p</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Your backend is fast. Your database queries are optimized. You've minimized your JavaScript bundles and compressed your images.&lt;br&gt;
But your users are still downloading the same CSS file every single time they visit your site. They are re-fetching API responses that haven't changed in hours. Every page load makes unnecessary network requests that could have been avoided.&lt;/p&gt;

&lt;p&gt;The problem? You're not leveraging HTTP caching headers.&lt;br&gt;
HTTP caching is one of the most powerful performance optimizations available to web developers, yet it's surprisingly misunderstood. When configured correctly, it can dramatically reduce server load, decrease bandwidth costs, and make your application feel instantaneous to users.&lt;/p&gt;

&lt;p&gt;And the best part? It requires no changes to your frontend code. It's all about telling the browser: "You already have this. Don't ask me again."&lt;/p&gt;

&lt;p&gt;This guide covers the essential caching headers you need to know, how to implement them in Node.js/Express.&lt;/p&gt;

&lt;p&gt;Let's go!!.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Caching?
&lt;/h3&gt;

&lt;p&gt;Before we dive into HTTP-specific caching, let us establish what caching actually means.&lt;/p&gt;

&lt;p&gt;Caching is the practice of storing copies of data in a temporary storage location so that future requests for that data can be served faster.&lt;/p&gt;

&lt;p&gt;An analogy? Instead of walking to the library every time you need to reference a book, you photocopy the pages you need and keep them at your desk. The next time you need that information, you just look at your copy instead of making another trip.&lt;/p&gt;

&lt;p&gt;In web development, caching happens at multiple levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Browser Cache (Client-Side):&lt;/strong&gt;
The user's browser stores copies of files (CSS, JavaScript, images, API responses) locally on their device. When they revisit your site, the browser can serve these files from disk instead of downloading them again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN Cache (Edge):&lt;/strong&gt;
Content Delivery Networks store copies of your static assets on servers distributed around the world. When a user in Tokyo requests your site hosted in New York, the CDN serves files from a server closer to Tokyo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Cache (Backend):&lt;/strong&gt;
Your server might cache database query results, rendered HTML, or computed values in memory (Redis, Memcached) to avoid expensive recalculations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Cache:&lt;/strong&gt;
Databases cache frequently accessed data in memory to avoid slow disk reads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this article, let's focus on the first level: browser caching controlled by HTTP headers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why HTTP Caching Matters
&lt;/h3&gt;

&lt;p&gt;Consider a typical website visit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without caching:&lt;/strong&gt;&lt;br&gt;
User visits your site, browser downloads: HTML (50KB), CSS (100KB), JavaScript (300KB), images (2MB) making it a total of 2.45MB downloaded. If the user clicks to another page, browser downloads everything again: 2.45MB. If the user refreshes the page, browser downloads everything again: 2.45MB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With proper caching:&lt;/strong&gt;&lt;br&gt;
User visits your site, browser downloads: 2.45MB on first visit, user clicks to another page, browser uses cached files: 0KB downloaded (just new HTML). If the user refreshes the page, browser uses cached files: 0KB downloaded.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Faster page loads (files served from local disk, not network).&lt;/li&gt;
&lt;li&gt;Reduced server load (fewer requests hitting your backend).&lt;/li&gt;
&lt;li&gt;Lower bandwidth costs (less data transferred).&lt;/li&gt;
&lt;li&gt;Better user experience (pages feel instant).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Two Types of HTTP Caching
&lt;/h3&gt;

&lt;p&gt;HTTP caching works in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Freshness-based caching (Expiration):&lt;/strong&gt;&lt;br&gt;
Here, the server tells a browser that the file is fresh for a particular point of time. The browser stores the file and serves it directly from cache without making any network request until the expiration time. The headers involved are &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation-based caching (Conditional Requests)&lt;/strong&gt;&lt;br&gt;
The server tells the browser: "Here's the file and a fingerprint (ETag). Next time you need this file, send me the fingerprint. If it hasn't changed, I will tell you to use your cached version."&lt;br&gt;
The browser still makes a request, but if nothing changed, the server responds with "304 Not Modified" and no file data, saving bandwidth. The headers involved are &lt;code&gt;ETag&lt;/code&gt;, &lt;code&gt;If-None-Match&lt;/code&gt;, &lt;code&gt;Last-Modified&lt;/code&gt;, &lt;code&gt;If-Modified-Since&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can (and often should) use both types together.&lt;/p&gt;

&lt;p&gt;Here's a simple example:&lt;/p&gt;

&lt;p&gt;The first visit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Browser -&amp;gt; Server: GET /style.css
Server -&amp;gt; Browser: 200 OK
                   Cache-Control: max-age=86400
                   ETag: "abc123"
                   [CSS file content]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second Visit (within 24hours)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser -&amp;gt; Server: (no request made, serves from cache)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third Visit (after 24hours)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Browser -&amp;gt; Server: GET /style.css
                   If-None-Match: "abc123"
Server -&amp;gt; Browser: 304 Not Modified
                   (no file content sent)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser knows the file is still valid and uses its cached copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  How HTTP Caching Works: The Request/Response Cycle
&lt;/h3&gt;

&lt;p&gt;Let's trace through exactly what happens during HTTP requests and how caching headers control browser behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Basic Flow Without Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you visit a website without any caching headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Browser: "I need /app.js"
   -&amp;gt; Makes network request to server

2. Server: "Here's app.js"
   -&amp;gt; Sends 200 OK with file content (500KB)

3. Browser: "I need /app.js again" (user refreshes page)
   -&amp;gt; Makes network request to server

4. Server: "Here's app.js again"
   -&amp;gt; Sends 200 OK with file content (500KB)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every request downloads the full file. Wasteful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Flow With Freshness-Based Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the server sends Cache-Control: &lt;code&gt;max-age=3600&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Browser: "I need /app.js"
   -&amp;gt; Makes network request to server

2. Server: "Here's app.js, and it's fresh for 1 hour"
   -&amp;gt; Sends 200 OK
   -&amp;gt; Cache-Control: max-age=3600
   -&amp;gt; File content (500KB)

3. Browser stores:
   -&amp;gt; The file content
   -&amp;gt; The cache time (1 hour from now)

4. Browser: "I need /app.js again" (5 minutes later)
   -&amp;gt; Checks cache
   -&amp;gt; Sees file is still fresh (55 minutes remaining)
   -&amp;gt; NO NETWORK REQUEST MADE
   -&amp;gt; Serves from disk cache (0KB transferred)

5. Browser: "I need /app.js again" (65 minutes later)
   -&amp;gt; Checks cache
   -&amp;gt; Sees file is stale (expired)
   -&amp;gt; Makes network request to server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Flow With Validation-Based Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the server sends an ETag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Browser: "I need /app.js"
   -&amp;gt; Makes network request to server

2. Server: "Here's app.js with fingerprint 'abc123'"
   -&amp;gt; Sends 200 OK
   -&amp;gt; ETag: "abc123"
   -&amp;gt; File content (500KB)

3. Browser stores:
   -&amp;gt; The file content
   -&amp;gt; The ETag "abc123"

4. Browser: "I need /app.js again"
   -&amp;gt; Makes network request to server
   -&amp;gt; Sends: If-None-Match: "abc123"

5a. Server checks: "Has app.js changed since 'abc123'?"
    -&amp;gt; No, it hasn't changed
    -&amp;gt; Sends 304 Not Modified
    -&amp;gt; NO FILE CONTENT sent (only headers, ~200 bytes)

5b. Browser:
    -&amp;gt; Receives 304
    -&amp;gt; Uses cached file (0KB file transfer)

OR

5a. Server checks: "Has app.js changed since 'abc123'?"
    -&amp;gt; Yes, it has changed (new ETag: "xyz789")
    -&amp;gt; Sends 200 OK
    → ETag: "xyz789"
    -&amp;gt; New file content (500KB)

5b. Browser:
    -&amp;gt; Receives 200 with new content
    -&amp;gt; Updates cache with new file and new ETag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser makes a request, but often gets a tiny 304 response instead of re-downloading the full file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combining Both&lt;/strong&gt;&lt;br&gt;
The best strategy uses both freshness and validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=3600
ETag: "abc123"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this powerful?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. First request:
   Browser -&amp;gt; Server: GET /app.js
   Server -&amp;gt; Browser: 200 OK
                      Cache-Control: max-age=3600
                      ETag: "abc123"
                      [500KB content]

2. Request within 1 hour:
   Browser -&amp;gt; (no request, serves from cache)
   Bandwidth used: 0KB
   Server load: 0 requests

3. Request after 1 hour (file unchanged):
   Browser -&amp;gt; Server: GET /app.js
                      If-None-Match: "abc123"
   Server -&amp;gt; Browser: 304 Not Modified
   Bandwidth used: ~200 bytes
   Server load: 1 request (but no file read/send)

4. Request after 1 hour (file changed):
   Browser -&amp;gt; Server: GET /app.js
                      If-None-Match: "abc123"
   Server -&amp;gt; Browser: 200 OK
                      ETag: "xyz789"
                      [500KB new content]
   Bandwidth used: 500KB
   Server load: 1 request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get the best of both worlds: no requests during the fresh period, and efficient validation after expiration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Cache Storage
&lt;/h3&gt;

&lt;p&gt;Where does the browser store cached files?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory Cache (RAM):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely fast.&lt;/li&gt;
&lt;li&gt;Cleared when you close the tab/browser.&lt;/li&gt;
&lt;li&gt;Used for resources needed during the current session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disk Cache (Hard Drive/SSD):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistent across browser sessions.&lt;/li&gt;
&lt;li&gt;Survives browser restarts.&lt;/li&gt;
&lt;li&gt;Larger storage capacity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser decides which cache to use based on various factors (file size, available memory, cache headers). You don't control this directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache-Control: The Primary Caching Header
&lt;/h3&gt;

&lt;p&gt;Cache-Control is the most important caching header. It controls whether resources are cached, who can cache them, and for how long.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Essential Directives:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;max-age=&lt;/strong&gt;&lt;br&gt;
How long the browser can use the cached version without checking with the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=86400
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is fresh for 86400 seconds (24 hours). The browser won't make any request during this time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is used for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static assets that rarely change (CSS, JS with versioned filenames).&lt;/li&gt;
&lt;li&gt;Images, fonts, videos.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;max-age=31536000: 1 year (maximum recommended for static assets).&lt;/li&gt;
&lt;li&gt;max-age=3600: 1 hour (good for semi-dynamic content).&lt;/li&gt;
&lt;li&gt;max-age=60: 1 minute (frequently changing data).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;no-cache&lt;/strong&gt;&lt;br&gt;
Forces the browser to validate with the server before using the cached version, even if it's fresh.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: no-cache
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Despite the name, this does NOT prevent caching. The file is still cached, but the browser must check with the server (via ETag) before using it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Content that changes frequently but you still want validation.&lt;/li&gt;
&lt;li&gt;HTML pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;no-store&lt;/strong&gt;&lt;br&gt;
Prevents caching entirely. The browser must download fresh every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: no-store
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing is saved to cache. Every request downloads the full file.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Sensitive data (banking info, private user data).&lt;/li&gt;
&lt;li&gt;Content that should never be stored locally.
&lt;strong&gt;Don't use for:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Static assets (terrible for performance).&lt;/li&gt;
&lt;li&gt;Public content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;public:&lt;/strong&gt;&lt;br&gt;
Allows any cache (browser, CDN, proxy) to store the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=31536000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Static assets served to all users.&lt;/li&gt;
&lt;li&gt;Public images, CSS, JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;private&lt;/strong&gt;&lt;br&gt;
Only the user's browser can cache this, not CDNs or shared caches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: private, max-age=3600
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;User-specific data (API responses with personal info).&lt;/li&gt;
&lt;li&gt;Authenticated content.&lt;/li&gt;
&lt;li&gt;Personalized HTML.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;must-revalidate&lt;/strong&gt;&lt;br&gt;
Once the cached version expires, the browser must check with the server before using it. Cannot serve stale content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=3600, must-revalidate
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Content where serving stale data would be problematic.&lt;/li&gt;
&lt;li&gt;Financial data, inventory counts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The immutable Directive&lt;/strong&gt;&lt;br&gt;
A special directive supported by modern browsers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=31536000, immutable
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;immutable tells the browser that the file won't change and doesn't need revalidation. This prevents unnecessary revalidation requests when users hit refresh.&lt;br&gt;
Only use this for files with hashed/versioned filenames like &lt;code&gt;app.a1b2c3.js&lt;/code&gt;. If the file changes, the filename changes, forcing a fresh download.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combining Directives&lt;/strong&gt;&lt;br&gt;
You can combine multiple directives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=31536000, immutable
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means: anyone can cache this, it's fresh for 1 year, and it will never change (immutable).&lt;/p&gt;

&lt;h3&gt;
  
  
  ETag &amp;amp; Validation: Efficient Change Detection
&lt;/h3&gt;

&lt;p&gt;ETags (Entity Tags) are fingerprints for resources. They let the browser ask "has this file changed?" without downloading it again.&lt;br&gt;
How ETags Work&lt;br&gt;
First request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/users

Response:
200 OK
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: no-cache
[response data]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server sends the file and its ETag (usually an MD5 hash of the content).&lt;/p&gt;

&lt;p&gt;Subsequent request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/users
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

Response (if unchanged):
304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
[no response data]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser sends the ETag. If the content hasn't changed, the server responds with 304 Not Modified and no data. The browser uses its cached version.&lt;br&gt;
If the file changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Response:
200 OK
ETag: "7f5c8e9a2b3d4c1f6e8a9b0c1d2e3f4a5b6c7d8e"
[new response data]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server sends the full file with a new ETag.&lt;/p&gt;

&lt;h4&gt;
  
  
  Strong vs Weak ETags
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Strong ETag:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indicates byte-for-byte identical content. Even a single character change produces a different ETag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weak ETag:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;ETag: W/"33a64df551425fcc55e4d42a148795d9f25f89d4"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefixed with W/. Indicates semantically equivalent content, but bytes might differ (e.g., whitespace changes, gzip compression levels).&lt;/p&gt;

&lt;p&gt;Use strong ETags for most cases. Weak ETags are for edge cases like dynamic compression.&lt;/p&gt;

&lt;h3&gt;
  
  
  Last-Modified Header (The Alternative)
&lt;/h3&gt;

&lt;p&gt;Before ETags, there was Last-Modified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser sends this back as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file hasn't been modified since that timestamp, the server responds with 304 Not Modified.&lt;br&gt;
ETags vs Last-Modified:&lt;/p&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;ETag&lt;/th&gt;
&lt;th&gt;Last-Modified&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Precision&lt;/td&gt;
&lt;td&gt;Exact (content-based)&lt;/td&gt;
&lt;td&gt;1-second granularity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Dynamic content, APIs&lt;/td&gt;
&lt;td&gt;Static files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Requires hash calculation&lt;/td&gt;
&lt;td&gt;Just timestamp check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;More accurate&lt;/td&gt;
&lt;td&gt;Can fail with clock skew&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use ETags for APIs and dynamic content. Use Last-Modified (or both) for static files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use ETags?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content changes unpredictably.&lt;/li&gt;
&lt;li&gt;You can't set long max-age times.&lt;/li&gt;
&lt;li&gt;Users need reasonably fresh data.&lt;/li&gt;
&lt;li&gt;The cost of serving stale content is high.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;HTML pages (need to check for updates, but don't want full re-downloads).&lt;/li&gt;
&lt;li&gt;API endpoints that change occasionally.&lt;/li&gt;
&lt;li&gt;User-generated content.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;You have long max-age times (no validation needed during fresh period).&lt;/li&gt;
&lt;li&gt;Every request needs fresh data anyway (just use &lt;code&gt;no-store&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Computing the ETag is expensive (defeats the performance benefit).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Code Examples in Node.js/Express
&lt;/h3&gt;

&lt;p&gt;Let's look at code snippets on how to implement caching. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set up Express&lt;/strong&gt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Caching Static Assets&lt;/strong&gt;&lt;br&gt;
For static files (CSS, JS, images), use aggressive caching:&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&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&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;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;immutable&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically adds &lt;code&gt;Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;&lt;br&gt;
Note: Only use this for versioned filenames like app.a1b2c3.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Response - Public Data&lt;/strong&gt;&lt;br&gt;
Cache public API data for 5 minutes:&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="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;/api/products&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;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=300&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;products&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="na"&gt;id&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="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;Product A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;29.99&lt;/span&gt; &lt;span class="p"&gt;},&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="mi"&gt;2&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;Product B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;49.99&lt;/span&gt; &lt;span class="p"&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&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;API Response - User-Specific Data&lt;/strong&gt;&lt;br&gt;
Cache user data privately for 10 minutes:&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="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;/api/user/profile&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;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;private, 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;123&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@example.com&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&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;HTML Pages - Always Validate&lt;/strong&gt;&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="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;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;no-cache&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;sendFile&lt;/span&gt;&lt;span class="p"&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;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&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&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;index.html&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;Express automatically generates ETags. The browser will validate on each request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing ETags Manually&lt;/strong&gt;&lt;br&gt;
For more control over validation:&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;crypto&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;crypto&lt;/span&gt;&lt;span class="dl"&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;/api/data&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;data&lt;/span&gt; &lt;span class="o"&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="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="na"&gt;message&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md5&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&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;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;if-none-match&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;etag&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;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;304&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&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;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;ETag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;etag&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;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;no-cache&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;Preventing Cache for Sensitive Data&lt;/strong&gt;&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="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;/api/banking/account&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;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;no-store, private&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;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15234.56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;accountNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;****1234&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&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;Reusable Cache Middleware&lt;/strong&gt;&lt;br&gt;
You can create a middleware to simplify setting &lt;code&gt;Cache-Control&lt;/code&gt;.&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;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&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="k"&gt;return &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="nx"&gt;next&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;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&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&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;directives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`max-age=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;duration&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;immutable&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mustRevalidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;must-revalidate&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;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="nx"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="nf"&gt;next&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;/api/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&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;posts&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="na"&gt;id&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First Post&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;id&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Second Post&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&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;/api/user/settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;private&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="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;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&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;notifications&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settings&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;
  
  
  Quick Reference Table
&lt;/h3&gt;

&lt;p&gt;Below is a quick reference of resource types and which type of cache directives to use.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource Type&lt;/th&gt;
&lt;th&gt;Cache-Control&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Versioned CSS/JS&lt;/td&gt;
&lt;td&gt;public, max-age=31536000, immutable&lt;/td&gt;
&lt;td&gt;1 year&lt;/td&gt;
&lt;td&gt;Filename changes on update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unversioned images&lt;/td&gt;
&lt;td&gt;public, max-age=86400&lt;/td&gt;
&lt;td&gt;1 day&lt;/td&gt;
&lt;td&gt;Use shorter for frequently updated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML pages&lt;/td&gt;
&lt;td&gt;no-cache&lt;/td&gt;
&lt;td&gt;Validate each time&lt;/td&gt;
&lt;td&gt;Allow caching with ETag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public API (slow changes)&lt;/td&gt;
&lt;td&gt;public, max-age=3600&lt;/td&gt;
&lt;td&gt;1 hour&lt;/td&gt;
&lt;td&gt;Adjust based on update frequency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public API (frequent changes)&lt;/td&gt;
&lt;td&gt;public, max-age=300&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;Good default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User data&lt;/td&gt;
&lt;td&gt;private, max-age=600&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;td&gt;Private ensures no CDN caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time data&lt;/td&gt;
&lt;td&gt;public, max-age=5&lt;/td&gt;
&lt;td&gt;5 seconds&lt;/td&gt;
&lt;td&gt;Or use no-store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensitive data&lt;/td&gt;
&lt;td&gt;no-store, private&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;td&gt;Banking, health records&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Every unnecessary network request is a missed opportunity. Every repeated download of the same CSS file wastes time and bandwidth. HTTP caching headers solve this. They tell browsers to reuse resources that haven't changed, eliminating redundant requests and making your site feel instant.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>systemdesign</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>useOptimistic: Build Snappier React UIs with Optimistic Updates</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Tue, 03 Mar 2026 15:00:51 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/useoptimistic-build-snappier-react-uis-with-optimistic-updates-3ijd</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/useoptimistic-build-snappier-react-uis-with-optimistic-updates-3ijd</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Have you ever clicked a like button on a social media post and watched it... do nothing for a second? Maybe it shows a loading spinner. Maybe the button just sits there, unresponsive, while your click travels to the server and back.&lt;/p&gt;

&lt;p&gt;Now compare that to Instagram or Twitter. You tap the heart icon, and it instantly turns red. No waiting. No spinners. The UI responds immediately, making the app feel lightning-fast even if your network connection isn't.&lt;/p&gt;

&lt;p&gt;That is an optimistic update. The UI assumes your action will succeed and updates immediately, while the actual server request happens in the background. If something goes wrong, it quietly rolls back.&lt;/p&gt;

&lt;p&gt;React 19 introduced the &lt;code&gt;useOptimistic&lt;/code&gt; hook to make this pattern easy to implement. Before this hook, you had to manually manage optimistic state, handle rollbacks, and coordinate between local UI state and server state. It was doable, but messy. With &lt;code&gt;useOptimistic&lt;/code&gt;, you get instant UI feedback with just a few lines of code.&lt;/p&gt;

&lt;p&gt;In this article, we will build a real example from scratch, compare it side-by-side with the traditional &lt;code&gt;useState&lt;/code&gt; approach and learn when you should (and shouldn't) use optimistic updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why useOptimistic?
&lt;/h3&gt;

&lt;p&gt;Before we write build an example, let's understand where optimistic updates shine. These are scenarios where the user action is highly likely to succeed and waiting for server confirmation creates unnecessary friction.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Social Interactions (Likes and Comments):&lt;/strong&gt;&lt;br&gt;
When a user likes a post, they expect the heart to turn red instantly. If there is a 500ms round-trip delay to the database, the app feels "heavy." Since a "Like" rarely fails due to business logic, it's the perfect candidate for an optimistic update.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Task Management (To-Do Lists or Kanban):&lt;/strong&gt;&lt;br&gt;
Checking off a task or moving a card between columns should feel tactile. Using &lt;code&gt;useOptimistic&lt;/code&gt;, the task moves immediately. If the server eventually returns an error (e.g., a lost connection), the hook automatically rolls the UI back to its previous state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time Messaging:&lt;/strong&gt;&lt;br&gt;
Modern chat apps don't show a "sending..." spinner for every single message. They append the message to the chat window immediately with a slightly faded style. Once the server confirms, the style updates to "sent."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The use cases are not limited to the three above. But you will quickly notice that &lt;code&gt;useOptimistic&lt;/code&gt; works best when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The operation is likely to succeed.&lt;/li&gt;
&lt;li&gt;The rollback is simple and safe.&lt;/li&gt;
&lt;li&gt;The operation is not critical.&lt;/li&gt;
&lt;li&gt;Instant feedback will greatly improve UX.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setting up a project
&lt;/h3&gt;

&lt;p&gt;Now for the fun part, Let's create a fresh React app with TypeScript using Vite. We will keep it minimal, no unnecessary dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a new Vite project with React and TypeScript
npm create vite@latest useoptimistic-demo -- --template react-ts

# Navigate into the project
cd useoptimistic-demo

# Install dependencies
npm install

# Start the dev server
npm run dev

Your app should now be running at http://localhost:5173.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up the project folder in your favorite code editor, then delete the boilerplate files/folders: &lt;code&gt;src/assets&lt;/code&gt;, &lt;code&gt;src/App.css&lt;/code&gt;, &lt;code&gt;src/index.css&lt;/code&gt;(we will use inline style for this). Also delete where they were imported from the remaining files.&lt;/p&gt;

&lt;p&gt;Let's build the bare UI. Replace what you have in your &lt;code&gt;src/App.tsx&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState } from "react";

const mockPost = {
  id: "post-1",
  author: "Jane Doe",
  content: "Just learned about useOptimistic in React 19!",
  likes: 42,
  isLiked: false,
};

function App() {
  const [post, setPost] = useState(mockPost);

  const handleLike = () =&amp;gt; {
    // Logic goes here
  };

  return (
    &amp;lt;div
      style={{
        minHeight: "100vh",
        background: "#f5f5f5",
        padding: "2rem",
      }}
    &amp;gt;
      &amp;lt;div
        style={{
          maxWidth: "600px",
          margin: "0 auto",
        }}
      &amp;gt;
        &amp;lt;h1
          style={{
            marginBottom: "2rem",
            color: "#333",
            fontSize: "2rem",
          }}
        &amp;gt;
          useOptimistic
        &amp;lt;/h1&amp;gt;

        &amp;lt;div
          style={{
            background: "white",
            borderRadius: "12px",
            padding: "1.5rem",
          }}
        &amp;gt;
          &amp;lt;div
            style={{
              marginBottom: "1rem",
            }}
          &amp;gt;
            &amp;lt;h3
              style={{
                fontSize: "1rem",
                fontWeight: "600",
                color: "#333",
                margin: 0,
              }}
            &amp;gt;
              {post.author}
            &amp;lt;/h3&amp;gt;
          &amp;lt;/div&amp;gt;

          &amp;lt;p
            style={{
              color: "#555",
              lineHeight: "1.6",
              marginBottom: "1.5rem",
              fontSize: "0.95rem",
            }}
          &amp;gt;
            {post.content}
          &amp;lt;/p&amp;gt;

          &amp;lt;div
            style={{
              display: "flex",
              alignItems: "center",
              gap: "0.5rem",
            }}
          &amp;gt;
            &amp;lt;button
              onClick={handleLike}
              style={{
                background: "transparent",
                border: "none",
                cursor: "pointer",
                fontSize: "1.5rem",
                padding: "0.5rem",
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                transition: "transform 0.2s",
              }}
            &amp;gt;
              {post.isLiked ? "❤️" : "🤍"}
            &amp;lt;/button&amp;gt;
            &amp;lt;span
              style={{
                color: "#666",
                fontSize: "0.9rem",
                fontWeight: "500",
              }}
            &amp;gt;
              {post.likes} likes
            &amp;lt;/span&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have is just UI, nothing happens just yet. Feel free to make changes to the design if you'd like. &lt;/p&gt;

&lt;h3&gt;
  
  
  Using useState Hook
&lt;/h3&gt;

&lt;p&gt;Before we jump right in to &lt;code&gt;useOptimistic&lt;/code&gt;, I want us to have a feeling about how &lt;code&gt;useState&lt;/code&gt; alone will handle it. Replace your &lt;code&gt;handleLike&lt;/code&gt; function with this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const handleLike = async () =&amp;gt; {
    // We are simulating a network delay.
    await new Promise((resolve) =&amp;gt; setTimeout(resolve, 2000));

    setPost((prev) =&amp;gt; ({
      ...prev,
      likes: prev.likes + (prev.isLiked ? -1 : 1),
      isLiked: !prev.isLiked,
    }));
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What did you notice? We see that we had to wait for about 2 seconds before the "Like" heart turned red.&lt;/p&gt;

&lt;h3&gt;
  
  
  With useOptimistic Hook
&lt;/h3&gt;

&lt;p&gt;Now let's adapt our component to use the &lt;code&gt;useOptimistic&lt;/code&gt; hook. Replace your &lt;code&gt;App.tsx&lt;/code&gt; with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { startTransition, useOptimistic, useState } from "react";

const mockPost = {
  id: "post-1",
  author: "Jane Doe",
  content: "Just learned about useOptimistic in React 19!",
  likes: 42,
  isLiked: false,
};

function App() {
  const [post, setPost] = useState(mockPost);
  const [optimisticPost, setOptimisticPost] = useOptimistic(
    post,
    (currentPost, newLikeState: { isLiked: boolean; likes: number }) =&amp;gt; ({
      ...currentPost,
      ...newLikeState,
    }),
  );

  const handleLikeAction = async () =&amp;gt; {
    const newIsLiked = !optimisticPost.isLiked;
    const newLikes = optimisticPost.likes + (newIsLiked ? 1 : -1);

    startTransition(async () =&amp;gt; {
      setOptimisticPost({ isLiked: newIsLiked, likes: newLikes });

      // We are simulating a network delay.
      await new Promise((resolve) =&amp;gt; setTimeout(resolve, 2000));

      setPost((prev) =&amp;gt; ({
        ...prev,
        likes: prev.likes + (prev.isLiked ? -1 : 1),
        isLiked: !prev.isLiked,
      }));
    });
  };

  return (
    &amp;lt;div
      style={{
        minHeight: "100vh",
        background: "#f5f5f5",
        padding: "2rem",
      }}
    &amp;gt;
      &amp;lt;div
        style={{
          maxWidth: "600px",
          margin: "0 auto",
        }}
      &amp;gt;
        &amp;lt;h1
          style={{
            marginBottom: "2rem",
            color: "#333",
            fontSize: "2rem",
          }}
        &amp;gt;
          useOptimistic
        &amp;lt;/h1&amp;gt;

        &amp;lt;div
          style={{
            background: "white",
            borderRadius: "12px",
            padding: "1.5rem",
          }}
        &amp;gt;
          &amp;lt;div
            style={{
              marginBottom: "1rem",
            }}
          &amp;gt;
            &amp;lt;h3
              style={{
                fontSize: "1rem",
                fontWeight: "600",
                color: "#333",
                margin: 0,
              }}
            &amp;gt;
              {post.author}
            &amp;lt;/h3&amp;gt;
          &amp;lt;/div&amp;gt;

          &amp;lt;p
            style={{
              color: "#555",
              lineHeight: "1.6",
              marginBottom: "1.5rem",
              fontSize: "0.95rem",
            }}
          &amp;gt;
            {post.content}
          &amp;lt;/p&amp;gt;

          &amp;lt;div
            style={{
              display: "flex",
              alignItems: "center",
              gap: "0.5rem",
            }}
          &amp;gt;
            &amp;lt;button
              onClick={handleLikeAction}
              style={{
                background: "transparent",
                border: "none",
                cursor: "pointer",
                fontSize: "1.5rem",
                padding: "0.5rem",
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                transition: "transform 0.2s",
              }}
            &amp;gt;
              {optimisticPost.isLiked ? "❤️" : "🤍"}
            &amp;lt;/button&amp;gt;
            &amp;lt;span
              style={{
                color: "#666",
                fontSize: "0.9rem",
                fontWeight: "500",
              }}
            &amp;gt;
              {optimisticPost.likes} likes
            &amp;lt;/span&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you notice the change? Now the like state changes instantly as the user clicks. But let's understand what is happening. Let's analyze the &lt;code&gt;useOptimistic&lt;/code&gt; code block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const [optimisticPost, setOptimisticPost] = useOptimistic(
    post, // current state
    (currentPost, newLikeState: { isLiked: boolean; likes: number }) =&amp;gt; ({ // reducer function
      ...currentPost,
      ...newLikeState,
    }),
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hook receives two values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The current value/ state (passthrough):&lt;/strong&gt;&lt;br&gt;
This is the "source of truth." It is the value that will be returned whenever no optimistic update is in progress.This usually comes from your server data or a useState hook.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An optional reducer function (which takes in two arguments):&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;currentState: The current optimistic state (or the passthrough value if this is the first update).&lt;/li&gt;
&lt;li&gt;optimisticValue: The "action" or payload you passed to the trigger function. It doesn't have to be a boolean; it can be an object (just like we did), a string, or whatever piece of data you need to calculate the next state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the hook returns two values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;optimisticState (The Result):&lt;/strong&gt;&lt;br&gt;
This is the value you use in your JSX. If an update is pending (inside a transition), this is the value calculated by your reducer. If no update is pending, this automatically reverts to the passthrough (the real state).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;addOptimistic (The Trigger):&lt;/strong&gt;&lt;br&gt;
This is a function you call to kick off the update. It takes one argument: the optimisticValue (the payload) that you want to send to your reducer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you notice, you will see that we used a transition (&lt;code&gt;startTransition&lt;/code&gt;). Optimistic updates must happen inside a transition or a Server Action, this tells react when to start or stop the optimistic state (basically helps it to track the optimistic state).&lt;/p&gt;

&lt;p&gt;Now, one may ask, what happens when a network request fails? Let's find out. Change your &lt;code&gt;handleLikeAction&lt;/code&gt; function to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const handleLikeAction = async () =&amp;gt; {
    const newIsLiked = !optimisticPost.isLiked;
    const newLikes = optimisticPost.likes + (newIsLiked ? 1 : -1);

    startTransition(async () =&amp;gt; {
      setOptimisticPost({ isLiked: newIsLiked, likes: newLikes });

      try {
        // We are simulating a network delay.
        await new Promise((_, reject) =&amp;gt; setTimeout(reject, 2000));

        setPost((prev) =&amp;gt; ({
          ...prev,
          likes: prev.likes + (prev.isLiked ? -1 : 1),
          isLiked: !prev.isLiked,
        }));
      } catch (error) {
        console.error("Failed to update like status:", error);
      }
    });
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see the magic? When we clicked on like, the heart turns red instantly. But then our promise is rejected (simulating network failure) and immediately the state is reverted to unlike. This then tells you that react always compares the real state of our post object with that of the optimistic state. If the transition finishes and the two states are not the same (because our &lt;code&gt;setPost&lt;/code&gt; never ran due to an error), React realizes the "lie" was wrong and snaps the UI back to match the real state. You don't have to manually rollback. &lt;/p&gt;

&lt;p&gt;What you can do is to handle your error in the catch block either by showing an error toast or just logging on the console if there's no need to give a visual feedback of that error.&lt;/p&gt;

&lt;h3&gt;
  
  
  When NOT to Use useOptimistic
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;useOptimistic&lt;/code&gt; is powerful, it is not appropriate for every situation. Using it incorrectly can lead to poor user experience, data inconsistencies, or even dangerous bugs.&lt;/p&gt;

&lt;p&gt;Let's explore scenarios where you should avoid optimistic updates&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Financial Transactions (Payments, Transfers, Refunds):&lt;/strong&gt;&lt;br&gt;
Imagine clicking "Send 500 (in your local currency)" and the UI immediately shows "Payment sent", but the server rejects it due to insufficient funds. By the time the error appears and the UI rolls back, the user might have already closed the app, thinking the payment went through. Rather, show a clear loading state. Wait for server confirmation before updating the UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Destructive Actions (Delete, Archive, Permanent Changes):&lt;/strong&gt;&lt;br&gt;
Deleting something optimistically means it disappears from the UI immediately. If the deletion fails, the item reappears confusing users who might think it is a bug or duplicate. User's might even lose trust in your app's reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex Business Logic:&lt;/strong&gt;&lt;br&gt;
If the success of an action depends on server-side calculations that the client can't predict, skip it. For example, a Promo Code input. You can't optimistically assume a code is valid and apply a discount before the server validates it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So always check if failure is critical, destructive, or has external side effects. If that's the case, avoid &lt;code&gt;useOptimistic&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use &lt;code&gt;useOptimistic&lt;/code&gt; when&lt;/th&gt;
&lt;th&gt;Avoid &lt;code&gt;useOptimistic&lt;/code&gt; when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Action is highly likely to succeed.&lt;/td&gt;
&lt;td&gt;Action has a high chance of rejection.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The operation is non-critical (likes, reactions, follows)&lt;/td&gt;
&lt;td&gt;The operation is critical (payments, deletions, account changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The goal is perceived speed.&lt;/td&gt;
&lt;td&gt;The goal is absolute accuracy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server validation is simple.&lt;/td&gt;
&lt;td&gt;Server validation is complex.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Final Thought
&lt;/h3&gt;

&lt;p&gt;Phew... If you made to the end, congratulations cause it has been a long one. &lt;code&gt;useOptimistic&lt;/code&gt; is a small hook with a big impact. It transforms user interactions from slow and waiting to instant and delightful. The best part? It's not even about making your backend faster. It's about being smarter with how you present state changes to users. Start small. Add it to one feature. Feel the difference. Then expand from there. You can get the react's official documentation here &lt;a href="https://react.dev/reference/react/useOptimistic" rel="noopener noreferrer"&gt;useOptimistic react docs&lt;/a&gt;. Happy Coding :).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Git HEAD</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Tue, 17 Feb 2026 14:00:10 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/-igc</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/-igc</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/ugochukwu_nebolisa_99efd3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F1699622%2F9ddb95a9-5cb8-45e7-bae7-085847295243.png" alt="ugochukwu_nebolisa_99efd3"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/ugochukwu_nebolisa_99efd3/git-head-its-not-as-scary-as-it-sounds-3gkc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Git HEAD: It’s Not as Scary as It Sounds&lt;/h2&gt;
      &lt;h3&gt;Ugochukwu Nebolisa ・ Feb 17&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>git</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Git HEAD: It’s Not as Scary as It Sounds</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Tue, 17 Feb 2026 13:49:14 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/git-head-its-not-as-scary-as-it-sounds-3gkc</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/git-head-its-not-as-scary-as-it-sounds-3gkc</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Ever run a command and seen the message &lt;code&gt;You are in 'detached HEAD' state&lt;/code&gt;? If you’re like me, your first instinct was probably to close the terminal and hope your computer didn't explode. But don't panic. HEAD is actually your best friend in Git.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Git HEAD
&lt;/h3&gt;

&lt;p&gt;Simply put, HEAD is a pointer to your current location in your Git repository. Think of it as a "You Are Here" sticker on your project’s map.&lt;/p&gt;

&lt;p&gt;More technically, HEAD is a reference that points to the current branch reference, which in turn points to the latest commit on that branch. When you make a new commit, Git updates your current branch to point to this new commit, and HEAD follows along.&lt;/p&gt;

&lt;p&gt;You can see where HEAD is pointing at any time by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git log --oneline -1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fmat5atnvxcofft2xkaoq.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%2Fmat5atnvxcofft2xkaoq.png" alt="git oneline output" width="719" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or you can get the full details by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat .git/HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ft82k1owofn3hiqlgfp96.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%2Ft82k1owofn3hiqlgfp96.png" alt="git/head output" width="719" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In most cases, you will see something like &lt;code&gt;ref: refs/heads/main&lt;/code&gt;, which means HEAD is pointing to the main branch (mine points to master). This is called a "symbolic reference" which means that HEAD points to a branch name, not directly to a commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  How HEAD Moves When You Work
&lt;/h3&gt;

&lt;p&gt;Understanding how HEAD moves is key to understanding Git itself. Let's look at the most common scenarios:&lt;/p&gt;

&lt;h4&gt;
  
  
  When You Make a Commit
&lt;/h4&gt;

&lt;p&gt;When you create a new commit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Git creates a new commit object with your changes&lt;/li&gt;
&lt;li&gt;Git updates your current branch to point to this new commit&lt;/li&gt;
&lt;li&gt;HEAD (which points to your branch) automatically follows along&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember our first commit? &lt;br&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%2Fmat5atnvxcofft2xkaoq.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%2Fmat5atnvxcofft2xkaoq.png" alt="git oneline output" width="719" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see that the HEAD was pointing to that very first commit. What if we add another one then run the same command &lt;code&gt;git log --oneline -1&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Make a change and commit
git add .
git commit -m "new feature"

# We will see that HEAD has moved to that commit
git log --oneline -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F07zeskljx9hw359o21wo.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%2F07zeskljx9hw359o21wo.png" alt="git oneline output" width="719" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now see that HEAD points to the latest commit in our current branch.&lt;/p&gt;

&lt;h4&gt;
  
  
  What happens when we switch branches?
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Let's create and checkout a branch
git checkout -b feature-branch

# Verify which branch we are on
git branch

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fk32j0yawht0a2gv6zky7.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%2Fk32j0yawht0a2gv6zky7.png" alt="Git branch output" width="719" height="102"&gt;&lt;/a&gt;&lt;br&gt;
We see that we are at our newly created branch. Let's now run &lt;code&gt;cat .git/HEAD&lt;/code&gt; and see where our HEAD points to.&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%2F88sysbelrulzsrqp39oe.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%2F88sysbelrulzsrqp39oe.png" alt=".git/HEAD output" width="719" height="102"&gt;&lt;/a&gt;&lt;br&gt;
Nice, we see that our head points to the current checkout branch.&lt;/p&gt;
&lt;h3&gt;
  
  
  HEAD, Working Directory, and Staging Area: What's the Difference?
&lt;/h3&gt;

&lt;p&gt;HEAD represents your last committed state, the snapshot of your project as it was when you last committed (We've understood this one already).&lt;/p&gt;

&lt;p&gt;The Working Directory is what you actually see in your file system right now. The files you are actively editing. Changes here are untracked by Git until you explicitly tell it about them.&lt;/p&gt;

&lt;p&gt;The Staging Area (Index) is a middle ground. A "preparation zone" where you place changes that you intend to include in your next commit. When you run &lt;code&gt;git add&lt;/code&gt;, you are moving changes from your working directory into the staging area.&lt;/p&gt;
&lt;h3&gt;
  
  
  Detached HEAD State
&lt;/h3&gt;

&lt;p&gt;Have you seen this message before? &lt;br&gt;
&lt;code&gt;You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It's not as scary as it looks. Remember how we said HEAD normally points to a branch, which then points to a commit? In detached HEAD state, that middle step is skipped. HEAD points directly to a commit instead of a branch. So instead of HEAD to point to the branch the commit belongs to, it points directly to that commit instead of the branch. &lt;/p&gt;

&lt;p&gt;How do you end up in a detached HEAD? &lt;br&gt;
Simple, run the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout &amp;lt;commit ID/Hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fy3ya6zsepazuwshuubmz.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%2Fy3ya6zsepazuwshuubmz.png" alt="Detached HEAD" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happens when we run this command &lt;code&gt;cat .git/HEAD&lt;/code&gt; again?&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%2Ff5mysic80s0gfckhhmmz.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%2Ff5mysic80s0gfckhhmmz.png" alt="HEAD" width="800" height="58"&gt;&lt;/a&gt;&lt;br&gt;
We see that it return a commit hash instead of a branch name, meaning that our HEAD now points to the commit.&lt;/p&gt;
&lt;h4&gt;
  
  
  What can you do in a detached HEAD?
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;You can simply make experimental changes and if you don't really need those changes, you can checkout to any available branches. Git will eventually garbage collect those commits and they will be lost. This is the only genuine risk of detached HEAD state.&lt;/li&gt;
&lt;li&gt;If you have made those experimental changes and you feel you would love to retain them, you can simply checkout a new branch from your current state &lt;code&gt;git checkout -b &amp;lt;new-branch&amp;gt;&lt;/code&gt;.This "attaches" your detached HEAD commits to a real branch, so they won't be lost.&lt;/li&gt;
&lt;li&gt;What if you checkout to an available branch mistakenly? Does it mean that the changes you made in a detached HEAD is gone? Don't worry yet, Git keeps a log of everywhere HEAD has been. Run &lt;code&gt;git reflog&lt;/code&gt; and we will get something like this:&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%2F14zin6i40rpbu011i5eb.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%2F14zin6i40rpbu011i5eb.png" alt="git reflog" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find the commit hash of your lost work then run &lt;code&gt;git checkout -b recovery-branch &amp;lt;commit-hash&amp;gt;&lt;/code&gt;&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%2Fxzcodctoa70avy15j8qj.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%2Fxzcodctoa70avy15j8qj.png" alt="recovery" width="786" height="63"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;You now have a solid understanding of one of Git's most fundamental concepts. Before we finish, let's connect HEAD to a command you have probably seen or used before and now you will understand exactly why it works.&lt;/p&gt;

&lt;p&gt;You may have seen this command suggested as a way to undo your last commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git reset HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you understand HEAD, you know why this command works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HEAD: your current commit (the one you just made)&lt;/li&gt;
&lt;li&gt;~1: "one commit before"&lt;/li&gt;
&lt;li&gt;git reset HEAD~1: "move HEAD back one commit"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git simply moves your branch pointer and HEAD along with it, back to the previous commit, effectively undoing your last commit while keeping your changes in the working directory.&lt;br&gt;
The same logic applies to HEAD~2 (two commits back), HEAD~3, and so on. Once you see HEAD as a pointer you can do arithmetic on, a whole family of Git commands suddenly start to make sense.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Git HEAD is one of those concepts that quietly underpins almost everything you do in Git. Once you internalize that it is just a pointer,one that you can move, inspect, and reason about, commands that once felt daunting to understand suddenly becomes easy.&lt;/p&gt;

&lt;p&gt;The next time you see a detached HEAD warning or reach for &lt;code&gt;git reset&lt;/code&gt;, you will know exactly what's happening under the hood.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git commit -m "Happy commiting :)"&lt;/code&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Broke My Portfolio: The Debugger Protocol</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Sat, 31 Jan 2026 11:04:38 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/i-broke-my-portfolio-the-debugger-protocol-2dgf</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/i-broke-my-portfolio-the-debugger-protocol-2dgf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;Hi, I'm a Software Engineer who builds web applications from database to deployment. With a strong foundation in React and Node.js, I create seamless user experiences backed by robust APIs and efficient data systems. I enjoy solving real problems through code. I built a puzzle portfolio because I wanted to express that engineering is about solving problems, not just writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://the-debugger-portfolio-899943784474.us-west1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;Live Demo: &lt;a href="https://the-debugger-portfolio-899943784474.us-west1.run.app" rel="noopener noreferrer"&gt;https://the-debugger-portfolio-899943784474.us-west1.run.app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The concept was simple: The portfolio loads in a "Critical Failure" state. The user (the recruiter) acts as the Lead Engineer and must drag-and-drop the correct technology chips to "patch" the bugs and reveal my projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stcak
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Frontend: Next.js (App Router) for the framework.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Styling: Tailwind CSS. I leaned heavily into a "Cyberpunk/Terminal" aesthetic to sell the "System Failure" vibe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State Management: Zustand. I needed a robust state machine to handle the sequential leveling system (Intro -&amp;gt; Level 1 -&amp;gt; Level 2 -&amp;gt; Level 3 -&amp;gt; Victory).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Interactivity: react-dnd for the drag-and-drop mechanics.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Powered by Google AI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Gemini API (The AI Assistant): I integrated the Gemini API to act as a "System AI." If a user struggles to fix a bug (e.g., they try to use MongoDB to fix a Real-Time WebSocket error), Gemini analyzes the game state and provides a context-aware hint. It explains why that tech was the wrong choice, effectively teaching the user about my stack while they play.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google AI Studio: I used Gemini 3 Pro (Preview) in AI Studio to help architect the "Game State" logic. It helped me map out the complex relationships between the error messages and the valid solutions for my specific projects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;The entire application is containerized using Docker and deployed to Google Cloud Run. This ensures that the animation-heavy UI runs smoothly and scales automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;p&gt;First and foremost, I am really proud and amazed of how far AI has come. With just a simple prompt, one can achieve a lot of things (the limitation is just your mind).&lt;/p&gt;

&lt;p&gt;I'm also proud of the "Narrative Logic" of the game. I didn't just throw random bugs at the user. Each level corresponds to the actual architecture of my projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Level 1 (Save-It): You have to fix the Real-Time layer using Ably/Firebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Level 2 (Brij): You have to fix the Fullstack Routing using Next.js.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Level 3 (Graso): You have to fix the Asset Tokenization using Sui Move.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It forces the user to understand the architecture of my projects to win. By the time they unlock the final screen, they don't just know what I built—they understand how I built it.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Building a Message-to-SQL AI Agent with Mastra and Telex.im</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Sun, 02 Nov 2025 20:19:24 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/building-a-message-to-sql-ai-agent-with-mastra-and-telexim-5hcd</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/building-a-message-to-sql-ai-agent-with-mastra-and-telexim-5hcd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As developers, we spend a significant amount of time writing SQL queries. Whether it's a simple SELECT statement or a complex JOIN with multiple aggregations, remembering the exact syntax can be tedious. What if you could just describe what you want in plain English and get production-ready SQL?&lt;/p&gt;

&lt;p&gt;That's exactly what I built for the HNG Stage 3 Backend Task - a Message-to-SQL AI Agent that translates natural language into SQL queries using Mastra AI and integrated with Telex.im.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every developer faces these challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Syntax Complexity&lt;/strong&gt;: SQL has different dialects (PostgreSQL, MySQL, SQLite, etc.) with subtle differences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Practices&lt;/strong&gt;: It's easy to write working SQL that performs poorly or has security issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Switching&lt;/strong&gt;: Moving between thinking about business logic and remembering SQL syntax breaks flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: SQL injection vulnerabilities are still one of the most common security issues&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I built an AI agent that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Converts natural language to SQL queries&lt;/li&gt;
&lt;li&gt;Validates syntax and suggests best practices&lt;/li&gt;
&lt;li&gt;Explains complex queries in plain English&lt;/li&gt;
&lt;li&gt;Optimizes queries for better performance&lt;/li&gt;
&lt;li&gt;Works with multiple SQL dialects&lt;/li&gt;
&lt;li&gt;Integrates seamlessly with Telex.im for team collaboration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: &lt;a href="https://mastra.ai" rel="noopener noreferrer"&gt;Mastra AI&lt;/a&gt; - A powerful TypeScript framework for building AI agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM&lt;/strong&gt;: Grok llama-3.1-8b-instant for natural language understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Platform&lt;/strong&gt;: &lt;a href="https://telex.im" rel="noopener noreferrer"&gt;Telex.im&lt;/a&gt; - An AI agent platform like Make.com, but specifically designed for communities, bootcamps, and educational environments. Think of it as a Slack alternative where AI agents work alongside humans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: TypeScript with Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: LibSQL for agent memory and observability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try It Live!
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://telex.im/telex-ai-intergration/home/colleagues/019a4481-0941-7922-bd91-3d525050223b/019a4480-adb3-7919-8603-2967ef5e6f21" rel="noopener noreferrer"&gt;Try the SQL Agent on Telex&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This agent translates your natural language questions into production-ready SQL queries. Just ask it something like "Get all users who registered in the last 30 days" and it will generate optimized SQL with security checks, formatting, and explanations&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Mastra?
&lt;/h3&gt;

&lt;p&gt;Mastra made this project straightforward because it provides:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Agent Framework&lt;/strong&gt;: No need to build from scratch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool System&lt;/strong&gt;: Easy to create specialized tools for SQL operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow Management&lt;/strong&gt;: Clean way to orchestrate multi-step processes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory &amp;amp; Context&lt;/strong&gt;: Built-in conversation memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: Out-of-the-box tracing and monitoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring System&lt;/strong&gt;: Quality evaluation for agent outputs&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Specific Mastra Features Used
&lt;/h3&gt;

&lt;p&gt;Here are the key Mastra features I leveraged in this project:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Agent Framework (&lt;code&gt;@mastra/core/agent&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&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;Agent&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="s1"&gt;@mastra/core/agent&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;sqlAgent&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;Agent&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;SQL Generator Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instructions&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="c1"&gt;// Detailed system prompt&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;groq/llama-3.1-8b-instant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// LLM configuration&lt;/span&gt;
  &lt;span class="na"&gt;tools&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="c1"&gt;// Custom tools&lt;/span&gt;
  &lt;span class="na"&gt;scorers&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="c1"&gt;// Quality evaluation&lt;/span&gt;
  &lt;span class="na"&gt;memory&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;Memory&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="c1"&gt;// Conversation context&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gave me a production-ready agent with just a few lines of configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Tool System (&lt;code&gt;@mastra/core/tools&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&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;createTool&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="s1"&gt;@mastra/core/tools&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;sqlValidatorTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'Validates and formats SQL queries',&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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="c1"&gt;// Zod validation&lt;/span&gt;
  &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&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="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;The tool system made it easy to extend the agent with specialized SQL capabilities.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Workflow Orchestration (&lt;code&gt;@mastra/core/workflows&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&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;createStep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createWorkflow&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="s1"&gt;@mastra/core/workflows&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;sqlWorkflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWorkflow&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql-workflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;generateSQLStep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workflows allowed me to chain multiple steps and handle complex logic flows.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;Quality Scoring (&lt;code&gt;@mastra/core/scores&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&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;createScorer&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="s1"&gt;@mastra/core/scores&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;sqlCorrectnessScorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createScorer&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;SQL Correctness&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// LLM-as-judge&lt;/span&gt;
  &lt;span class="na"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;groq/llama-3.1-8b-instant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;instructions&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;run&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="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;analyze&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="nf"&gt;generateScore&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;results&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="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateReason&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&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="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;The scoring system uses LLM-as-judge to evaluate output quality automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. &lt;strong&gt;Memory &amp;amp; Context (&lt;code&gt;@mastra/memory&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&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;Memory&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="s1"&gt;@mastra/memory&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;LibSQLStore&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="s1"&gt;@mastra/libsql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nl"&gt;memory&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;Memory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;storage&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;LibSQLStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file:../mastra.db&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;Built-in memory storage maintains conversation context across interactions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Agent
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Project Setup
&lt;/h3&gt;

&lt;p&gt;First, I initialized the project with Mastra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create mastra@latest &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/mastra/
├── agents/
│   └── sql-agent.ts       # Main agent definition
├── tools/
│   └── sql-tool.ts        # SQL tools
├── scorers/
│   └── sql-scorer.ts      # Quality evaluation
├── workflows/
│   └── sql-workflow.ts    # Workflow orchestration
└── index.ts               # Mastra configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Creating SQL Tools
&lt;/h3&gt;

&lt;p&gt;I built four specialized tools to enhance the agent's capabilities:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. SQL Validator Tool
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlValidatorTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sql-validator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Validates and formats SQL queries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql&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="s2"&gt;mysql&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="s2"&gt;sqlite&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="s2"&gt;mssql&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="s2"&gt;oracle&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="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="na"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&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="nf"&gt;validateSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dialect&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL syntax correctness&lt;/li&gt;
&lt;li&gt;Dangerous patterns (missing WHERE in UPDATE/DELETE)&lt;/li&gt;
&lt;li&gt;Best practice violations (SELECT *)&lt;/li&gt;
&lt;li&gt;Security vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Schema Info Tool
&lt;/h4&gt;

&lt;p&gt;Provides common database patterns and examples:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schemaInfoTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;schema-info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Provides common database schema patterns&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;tableType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;commonColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="na"&gt;relationships&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&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="nf"&gt;getSchemaInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableType&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;h4&gt;
  
  
  3. SQL Explainer Tool
&lt;/h4&gt;

&lt;p&gt;Breaks down complex queries into understandable components:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlExplainerTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sql-explainer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Explains what a SQL query does in plain English&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;part&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&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;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&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="nf"&gt;explainSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sql&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;h4&gt;
  
  
  4. SQL Optimizer Tool
&lt;/h4&gt;

&lt;p&gt;Suggests performance improvements:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlOptimizerTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sql-optimizer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Suggests optimizations for SQL queries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... implementation&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: Defining the Agent
&lt;/h3&gt;

&lt;p&gt;The agent ties everything together with clear instructions:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlAgent&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;Agent&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="s2"&gt;SQL Generator Agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
You are an expert SQL assistant that helps developers translate 
natural language queries into SQL statements.

Guidelines:
- Ask for clarification on table/column names if not specified
- Default to PostgreSQL syntax unless specified
- Use explicit JOIN syntax
- Avoid SELECT * in production queries
- Include security warnings for dangerous patterns
- Format with proper indentation
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groq/llama-3.1-8b-instant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sqlValidatorTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;schemaInfoTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sqlExplainerTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sqlOptimizerTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;scorers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sqlCorrectness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;intentMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;readability&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&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;memory&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;Memory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;storage&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;LibSQLStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file:../mastra.db&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Quality Scoring
&lt;/h3&gt;

&lt;p&gt;I implemented three scorers to ensure high-quality outputs:&lt;/p&gt;

&lt;h4&gt;
  
  
  SQL Correctness Scorer
&lt;/h4&gt;

&lt;p&gt;Uses groq/llama-3.1-8b-instant as a judge to evaluate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax correctness&lt;/li&gt;
&lt;li&gt;Best practices adherence&lt;/li&gt;
&lt;li&gt;Security issues
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlCorrectnessScorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createScorer&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="s2"&gt;SQL Correctness&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groq/llama-3.1-8b-instant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Evaluate SQL for syntax, best practices, and security...&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;run&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="cm"&gt;/* Extract SQL */&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="cm"&gt;/* Analyze quality */&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateScore&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;results&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="cm"&gt;/* Calculate score */&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateReason&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&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="cm"&gt;/* Explain score */&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Intent Match Scorer
&lt;/h4&gt;

&lt;p&gt;Ensures the generated SQL matches what the user actually wanted:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intentMatchScorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createScorer&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="s2"&gt;Intent Match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// Checks if SELECT query was generated for retrieval intent, etc.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Readability Scorer
&lt;/h4&gt;

&lt;p&gt;Evaluates formatting and documentation:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readabilityScorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createScorer&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="s2"&gt;SQL Readability&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// Checks formatting, indentation, comments&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Creating the Workflow
&lt;/h3&gt;

&lt;p&gt;The workflow orchestrates the entire process:&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;generateSQLStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStep&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generate-sql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Generates SQL query from natural language&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mastra&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mastra&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sqlAgent&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;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;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&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;responseText&lt;/span&gt; &lt;span class="o"&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;for&lt;/span&gt; &lt;span class="k"&gt;await &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;chunk&lt;/span&gt; &lt;span class="k"&gt;of&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;textStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;responseText&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;extractSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseText&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseText&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;sqlWorkflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWorkflow&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sql-workflow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql&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="s2"&gt;mysql&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="s2"&gt;sqlite&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="s2"&gt;mssql&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="s2"&gt;oracle&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="nf"&gt;optional&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;generateSQLStep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Integrating with Telex.im
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting Up the Integration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create an AI Co-Worker in Telex&lt;/strong&gt;&lt;br&gt;
In your Telex dashboard, navigate to the AI Co-Workers section and create a new co-worker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy the Agent&lt;/strong&gt;&lt;br&gt;
I deployed to Render for easy hosting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Workflow JSON&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"productivity"&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 project manager bot that tracks and summarizes tasks for Telex users."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unique_id_generated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"long_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;"You help users manage tasks and summarize chat history. Recognize project deadlines, remind users, and provide friendly nudges. Use clear, concise language and ask for missing details."&lt;/span&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;"project_helper_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"task_agent_node"&lt;/span&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;"Project helper bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"position"&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="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a2a/mastra-a2a-node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://your-domain.com/a2a/agent/sqlAgent"&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;span class="nl"&gt;"pinData"&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;"settings"&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;"executionOrder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v1"&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;"short_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;"Task/project manager for Telex"&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import to Telex&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Upload workflow JSON to Telex&lt;/li&gt;
&lt;li&gt;Activate the workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Testing the Integration
&lt;/h3&gt;

&lt;p&gt;The agent works beautifully in Telex! Here's a real conversation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "Get all users who registered in the last 30 days"&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&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="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'30 days'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&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;Explanation&lt;/strong&gt;: This query retrieves user information for accounts created in the last 30 days, sorted by most recent first. It uses explicit column selection (avoiding SELECT *) and includes proper date arithmetic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Note&lt;/strong&gt;: Use parameterized queries to prevent SQL injection when implementing this.&lt;/p&gt;




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

&lt;p&gt;Building this SQL agent taught me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AI agents are powerful when combined with specialized tools&lt;/li&gt;
&lt;li&gt;Mastra makes building production-ready agents surprisingly straightforward&lt;/li&gt;
&lt;li&gt;Quality scoring is essential for reliable outputs&lt;/li&gt;
&lt;li&gt;Integration platforms like Telex make agents accessible to teams&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The project is open source! Check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/benedictfred/hng-stage-3" rel="noopener noreferrer"&gt;hng-stage-3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telex Integration: &lt;a href="https://fynix.dev/blog/telex-x-mastra" rel="noopener noreferrer"&gt;instructions-link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mastra.ai/docs/" rel="noopener noreferrer"&gt;Mastra Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.telex.im" rel="noopener noreferrer"&gt;Telex.im A2A Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hng.tech" rel="noopener noreferrer"&gt;HNG Internship&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;Thanks to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/mastra" rel="noopener noreferrer"&gt;@mastra&lt;/a&gt; for the excellent AI framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/teleximapp" rel="noopener noreferrer"&gt;@teleximapp&lt;/a&gt; for the integration platform&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/hnginternship" rel="noopener noreferrer"&gt;@hnginternship&lt;/a&gt; for this challenging task&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What would you build with AI agents? Let me know in the comments!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you found this helpful, please share it with other developers building AI agents.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>mastra</category>
      <category>ai</category>
      <category>tooling</category>
      <category>node</category>
    </item>
    <item>
      <title>Building a Dynamic Profile API with Express.js and TypeScript — HNG Backend Stage Zero</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Thu, 16 Oct 2025 10:32:52 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/building-a-dynamic-profile-api-with-expressjs-and-typescript-hng-backend-stage-zero-j5</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/building-a-dynamic-profile-api-with-expressjs-and-typescript-hng-backend-stage-zero-j5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As part of the &lt;strong&gt;HNG Backend Stage 0&lt;/strong&gt; challenge, I built a RESTful API using &lt;strong&gt;Express.js&lt;/strong&gt; and &lt;strong&gt;TypeScript&lt;/strong&gt; that returns my profile information together with a random cat fact fetched dynamically from the &lt;a href="https://catfact.ninja/fact" rel="noopener noreferrer"&gt;Cat Facts API&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;This project helped me refine key backend concepts like API consumption, structured JSON responses, and handling asynchronous requests gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;The main goal was to create a &lt;code&gt;GET /me&lt;/code&gt; endpoint that outputs the following structure:&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&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;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your.email@example.com"&lt;/span&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;"Your Full Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stack"&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.js/Express"&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;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-10-16T12:34:56.789Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fact"&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 random cat fact from Cat Facts API"&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;This JSON response includes my details, a dynamically generated timestamp (in UTC ISO 8601 format), and a random cat fact fetched from an external API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;Here’s what I used to implement the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt;: Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: Express.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Client&lt;/strong&gt;: Axios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Additional Libraries&lt;/strong&gt;: CORS, Dotenv &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s the simplified version of the &lt;code&gt;/me&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express, { Request, Response } from 'express';
import axios from 'axios';
import dotenv from 'dotenv';

dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;

app.use((req, res, next) =&amp;gt; {
console.log([${new Date().toISOString()}] ${req.method} ${req.url});
next();
});

app.get('/me', async (req: Request, res: Response) =&amp;gt; {
const timestamp = new Date().toISOString();

let catFact = '';
try {
const { data } = await axios.get('https://catfact.ninja/fact', { timeout: 5000 });
catFact = data.fact;
} catch {
catFact = 'Could not fetch a cat fact at this moment.';
}

res.status(200).json({
status: 'success',
user: {
email: process.env.USER_EMAIL!,
name: process.env.USER_NAME!,
stack: process.env.USER_STACK!,
},
timestamp,
fact: catFact,
});
});

app.listen(PORT, () =&amp;gt; {
console.log(Server running on port ${PORT});
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing the API
&lt;/h2&gt;

&lt;p&gt;You can run and test the API locally by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use &lt;strong&gt;curl&lt;/strong&gt; or &lt;strong&gt;Postman&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&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%2Flc3jtr00wg7rgiafh7mr.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%2Flc3jtr00wg7rgiafh7mr.png" alt="PostMan Output" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;I deployed using &lt;strong&gt;PXXL App&lt;/strong&gt;, a free hosting platform. You can check out the live link here &lt;a href="https://hng-stage-zero.pxxl.click/me" rel="noopener noreferrer"&gt;Api Url&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This task helped me strengthen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error handling:&lt;/strong&gt; ensuring graceful fallbacks on API failure
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic data generation:&lt;/strong&gt; timestamps and per-request responses
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environmental configurations:&lt;/strong&gt; protecting sensitive data
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code readability:&lt;/strong&gt; through TypeScript interfaces and module separation
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The &lt;strong&gt;Stage 0 backend task&lt;/strong&gt; was a straightforward yet meaningful challenge. It blends creativity with structured engineering principles.&lt;br&gt;&lt;br&gt;
I’m excited for the next HNG stage and the opportunity to work with even more complex integrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect with Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/benedictfred" rel="noopener noreferrer"&gt;benedictfred&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/ugochukwu-nebolisa/" rel="noopener noreferrer"&gt;Ugochukwu Nebolisa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter/X:&lt;/strong&gt; &lt;a href="https://x.com/i_am_nebolisa" rel="noopener noreferrer"&gt;Nebolisa&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it brethren.Thanks for reading! If you’re also working on HNG tasks or even any project drop your comment below, let’s connect and grow together &lt;/p&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why I Want to Be a Front-End Developer and How HNG Will Help Me Achieve My Goals</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Thu, 30 Jan 2025 00:39:12 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/why-i-want-to-be-a-front-end-developer-and-how-hng-will-help-me-achieve-my-goals-3beh</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/why-i-want-to-be-a-front-end-developer-and-how-hng-will-help-me-achieve-my-goals-3beh</guid>
      <description>&lt;h2&gt;
  
  
  My Motivation for Becoming a Front-End Developer
&lt;/h2&gt;

&lt;p&gt;The world of technology has always fascinated me, but my journey into front-end development was driven by my passion for creating visually appealing and interactive web experiences. I always admired the idea of bringing designs to life, ensuring that users have seamless and engaging interactions with web applications. Seeing a well-structured interface respond dynamically to user input excites me, and I am committed to mastering this field to build functional, beautiful, and intuitive digital solutions.&lt;br&gt;
Then, I feel that frontend is a good way to get into tech not because it's really easy but because it's not boring since you see everything you are doing on the web. &lt;/p&gt;

&lt;h2&gt;
  
  
  How HNG Will Help Me Grow in Front-End Development
&lt;/h2&gt;

&lt;p&gt;The HNG Internship is an incredible opportunity to refine my skills and gain hands-on experience in front-end development. This intensive program offers mentorship from industry experts, real-world projects, and collaboration with talented developers. HNG's structured approach will help me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhance my technical skills:&lt;/strong&gt; I will sharpen my skills on modern frontend technologies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Work on real world projects:&lt;/strong&gt; HNG internship emphasizes on building real world projects, ensuring I learn the structure and format of a real world project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaborate with community:&lt;/strong&gt; The world of tech is all about collaboration with people with similar minds. Of course this internship will create the platform where I collaborate with other great minds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Get Mentorship and Feedback:&lt;/strong&gt; HNG Internship provides experienced mentors that will provide a guidance on how to navigate learning in order to prevent common learning pitfalls.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For companies looking for skilled professionals, you can &lt;a href="https://hng.tech/hire/reactjs-developers" rel="noopener noreferrer"&gt;Hire React.js Developers&lt;/a&gt; to bring top-tier front-end solutions to your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Goals for the HNG Internship &amp;amp; How I Plan to Achieve Them
&lt;/h2&gt;

&lt;p&gt;What is an internship without goals? To make the most of the HNG Internship, I have set clear goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Master Advanced Front-End Concepts:&lt;/strong&gt; I aim to deepen my understanding of frameworks like Next.js and improve my ability to build optimized and fast applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build a strong portfolio:&lt;/strong&gt; By working on diverse projects, I tend to improve my portfolio that showcases my skills and creativity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improve by problem solving skills:&lt;/strong&gt; By working on different projects, I will meet different problems and also learn different ways to solve them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expand My Professional Network:&lt;/strong&gt; "Your network is your net worth". Connecting with industry professionals and fellow interns will open doors for future collaborations and job opportunities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, for businesses in need of skilled professionals, you can &lt;a href="https://hng.tech/hire/web-developers" rel="noopener noreferrer"&gt;Hire Web Developers&lt;/a&gt; to elevate your digital presence with cutting-edge web solutions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Achieving success in any field is easier when you have the right platform. The HNG Internship provides an ideal environment for me to learn, grow, and ultimately become a top-tier front-end developer.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bootstrap vs Tailwind</title>
      <dc:creator>Ugochukwu Nebolisa</dc:creator>
      <pubDate>Fri, 28 Jun 2024 20:56:11 +0000</pubDate>
      <link>https://dev.to/ugochukwu_nebolisa_99efd3/bootstrap-vs-tailwind-4paf</link>
      <guid>https://dev.to/ugochukwu_nebolisa_99efd3/bootstrap-vs-tailwind-4paf</guid>
      <description>&lt;h2&gt;
  
  
  Bootstrap
&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%2F6zo8gs91osz9m92ocnu1.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%2F6zo8gs91osz9m92ocnu1.png" alt="Bootsrap" width="800" height="380"&gt;&lt;/a&gt;&lt;br&gt;
Bootstrap was developed by Mark Otto and Jacob Thornton at Twitter and was released in 2011. It is a free and open-source CSS framework which is aimed at a responsive and easy design of websites. Bootstrap comes with a set of pre-made components which enables one to design a website fast and with ease.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind
&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%2Ffopocbc56g31fnuxvwgn.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%2Ffopocbc56g31fnuxvwgn.png" alt="Tailwind" width="800" height="380"&gt;&lt;/a&gt;&lt;br&gt;
Tailwind is a utility-first CSS framework. It is a highly customizable and low-level CSS framework that gives you all of the building blocks you need to build designs. This framework has no ready made components rather allows you to build your own custom designs with the utility class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparisons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Philosophy:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bootstrap is a component-based framework which comes with a  ready made set of components for quick designing.&lt;/p&gt;

&lt;p&gt;Tailwind on the other hand is a utility-first framework which provides low-level utility classes that you can use to build custom designs without pre-defined components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bootstrap requires some big files to be incorporated in the project for development which occupies a larger space.&lt;/p&gt;

&lt;p&gt;Tailwind needs only style sheets to get the work done which takes lesser space.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Customization:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bootstrap is customizable but it involves overriding existing styles or using provided theme variables to change the look of components.&lt;/p&gt;

&lt;p&gt;Tailwind is fully customizable allowing developers to create components using the utility classes enabling granular control over over styles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Learning Curve:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bootstrap is easier to learn because of the provided component-based styles making it to be considered as more beginner-friendly CSS framework.&lt;/p&gt;

&lt;p&gt;Tailwind due to it's utility-first approach have a steeper learning curve because developers need an understanding of utility-first principles and building designs from scratch.    &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Community:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bootstrap has a well-established community with a well-defined documentations, plugins and themes providing good support to developers.&lt;/p&gt;

&lt;p&gt;Tailwind has a rapidly growing ecosystem with an active community support. Though newer than Bootstrap, it’s gaining popularity quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one to choose
&lt;/h2&gt;

&lt;p&gt;Personally, I would say that&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bootstrap&lt;/strong&gt; is best for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Quick prototyping and development with a consistent look and feel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers who prefer a predefined set of components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Beginners who are new to making websites.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tailwind&lt;/strong&gt; is best for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Developers who want complete control over their design and are comfortable building components from scratch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Projects requiring a unique design with a high degree of customization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams looking to optimize CSS file size with utility-first approaches and modern build tools.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Expectations in HNG
&lt;/h2&gt;

&lt;p&gt;I just got enrolled in HNG internship program. HNG Internship is a remote internship program designed to help young developers and designers improve their skills through practical, hands-on experience. I look forward to being an improved person after this internship and you can be too. &lt;br&gt;
For more enquiries, visit the &lt;a href="https://hng.tech/internship" rel="noopener noreferrer"&gt;HNG internship website&lt;/a&gt; and find out how you can &lt;a href="https://hng.tech/hire" rel="noopener noreferrer"&gt;hire enthusiastic interns&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
