<?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: Andrew David</title>
    <description>The latest articles on DEV Community by Andrew David (@emperorsixpacks).</description>
    <link>https://dev.to/emperorsixpacks</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%2F1015723%2F18ab138e-cadc-43e1-9cf1-546c4e3dd6a2.jpeg</url>
      <title>DEV Community: Andrew David</title>
      <link>https://dev.to/emperorsixpacks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emperorsixpacks"/>
    <language>en</language>
    <item>
      <title>How to Implement In-Memory Caching in GoLang</title>
      <dc:creator>Andrew David</dc:creator>
      <pubDate>Thu, 27 Nov 2025 13:18:15 +0000</pubDate>
      <link>https://dev.to/emperorsixpacks/how-to-implement-in-memory-caching-in-golang-5fa8</link>
      <guid>https://dev.to/emperorsixpacks/how-to-implement-in-memory-caching-in-golang-5fa8</guid>
      <description>&lt;h2&gt;
  
  
  The Why?
&lt;/h2&gt;

&lt;p&gt;I recently worked on Airtable OAuth authentication for a project and needed a place to store state and challenge code for later request validation. The obvious solution is to use a third-party solution like Redis (Which I am a big fan of), but I wanted to try out in-memory caching. Yes, I reviewed other tools like MemeCache, but am I not an engineer?&lt;/p&gt;

&lt;h2&gt;
  
  
  The What?
&lt;/h2&gt;

&lt;p&gt;When you start your Go app, Python app, or Node app, you can store certain data in the process, making it available throughout the program's lifetime. This data lives in the RAM (Random Access Memory), making it easily accessible by the program whenever it needs it. Most caching systems like Redis also store their data in RAM, but unlike Redis, an in-memory cache is not persistent.&lt;/p&gt;

&lt;p&gt;A simple in-memory cache typically maintains a key-value store in memory, tracks when each item should expire (using TTL — Time to Live), and optionally uses an LRU (Least Recently Used) policy to remove older items when a maximum size is reached. The cache runs in the same process as your application, making reads and writes extremely fast. However, because everything is stored in memory, all data is lost when the application stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less Talk, more code !!!
&lt;/h2&gt;

&lt;p&gt;Before we code, let’s go over a few things I think you should know&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LRU (Least Recently Used)&lt;/strong&gt; → My phone has a bunch of apps that I do not use—apps that have been on my phone for the longest time, occupying good space. LRU is an eviction policy that removes the least-used item in a cache. Essentially, think of it as removing that app you haven’t used in months so you can make room for the new game you want to install. Essentially, you are removing the oldest unused item, regardless of whether it has been accessed a lot in the past.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LFU (Least Frequently Used)&lt;/strong&gt; → Imagine this: your bag can only hold four books. If you get a new one, you'll need to remove a book to make room, and usually, you'll remove the book you read less. The same thing applies to an in-memory cache. LFU is an eviction policy that ranks items based on how often they’re used, and when space runs out, it removes the least-frequently-used item to make room for new ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TTL (Time To Live)&lt;/strong&gt; → This defines how long we want an item to stay in our cache. It's like setting an expiry date — once the TTL has elapsed, the item is considered stale and no longer valid. Any future attempts to get that item will fail or trigger a refresh. TTL is useful for keeping data fresh and automatically clearing out old or irrelevant entries without needing manual intervention or cleanup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache Hit, Cache Miss&lt;/strong&gt; → Whenever we request an item from the cache, a &lt;strong&gt;cache hit&lt;/strong&gt; occurs if the item exists and is still valid. A &lt;strong&gt;cache miss&lt;/strong&gt;, on the other hand, happens when the item is not found or has expired.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;So now we code, right?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Well, not just yet, there are still a few things we need to talk about, sorry :)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Design&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To build our cache, we’re going to keep things simple with just three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A map to store the items&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The cache itself&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Janitor to clean up our mess&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Now we code
&lt;/h3&gt;

&lt;p&gt;Let’s start by defining a very simple struct to hold all the data. After that, I’ll walk you through what each part represents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Cache Items&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;    &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Main cache struct&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;   &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;      &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RWMutex&lt;/span&gt;
    &lt;span class="n"&gt;janitor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Janitor&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// handles clean up&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Janitor&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;
    &lt;span class="n"&gt;stop&lt;/span&gt;     &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Item&lt;/code&gt; struct contains two fields: &lt;code&gt;Value&lt;/code&gt; and &lt;code&gt;Duration&lt;/code&gt;. Normally, you might use &lt;code&gt;time.Time&lt;/code&gt; for expiration, but here, &lt;code&gt;Duration&lt;/code&gt; is stored as an integer timestamp—a design choice to keep things simple and flexible.&lt;/p&gt;

&lt;p&gt;As for the &lt;code&gt;Cache&lt;/code&gt; struct, you might notice that it's unexported (made private). This allows us to expose a global constructor to create the cache instance. The real reason behind this design, though, is that I implemented it using the &lt;strong&gt;singleton pattern&lt;/strong&gt;—meaning I wanted to ensure that only one instance of the cache exists throughout the entire program.&lt;/p&gt;

&lt;p&gt;One thing I may have forgotten to mention is that our cache needs to be &lt;strong&gt;thread-safe&lt;/strong&gt;. This means it should prevent &lt;strong&gt;race conditions&lt;/strong&gt;, where two processes or goroutines try to modify the state of the cache at the same time.&lt;/p&gt;

&lt;p&gt;Go provides a solution for this through the use of a &lt;strong&gt;mutex&lt;/strong&gt;—a locking mechanism that ensures only one thread or goroutine can access a resource at a time. By locking the cache during operations, we prevent data corruption and maintain consistency.&lt;/p&gt;

&lt;p&gt;Another important component is the &lt;strong&gt;Janitor&lt;/strong&gt;. This is a lightweight scheduler that periodically runs in the background to clean up expired items from the cache. It helps free up memory and makes room for newer items, keeping the cache lean and efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expanding Item
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//Check if Item is expired&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Expired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnixNano&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method returns a &lt;code&gt;bool&lt;/code&gt; indicating whether an item has expired by checking if its timestamp (&lt;code&gt;Duration&lt;/code&gt;, remember) is less than the current time. But before doing that, it first checks if the &lt;code&gt;Duration&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;. If it is, the method returns &lt;code&gt;false&lt;/code&gt;, meaning the item should not expire.&lt;/p&gt;

&lt;p&gt;This is intentional. Sometimes, you may not want certain items in the cache to ever expire—you want them to persist for as long as the program is running. In this design, any item with a &lt;code&gt;Duration&lt;/code&gt; value of &lt;code&gt;0&lt;/code&gt; is treated as having &lt;strong&gt;no expiration&lt;/strong&gt;, and therefore is never evicted by the cleanup process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set
&lt;/h3&gt;

&lt;p&gt;Now we can begin working on our cache. The first method we’re going to add is &lt;code&gt;Set&lt;/code&gt;—this allows us to populate the cache with data that we can retrieve and use later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Store a value in cache with a defined ttl&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DefaultDuration&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnixNano&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Set&lt;/code&gt; method stores a value in the cache under a specific key, along with an optional TTL (Time To Live). If no TTL is provided, and the &lt;code&gt;DefaultDuration&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, the item will be stored &lt;strong&gt;without expiration&lt;/strong&gt;, meaning it will stay in the cache indefinitely unless manually removed.&lt;/p&gt;

&lt;p&gt;If a TTL is provided (i.e. greater than zero), it is converted to a Unix timestamp (&lt;code&gt;int64&lt;/code&gt;) for efficient comparison during retrieval or cleanup.&lt;/p&gt;

&lt;p&gt;Finally, the method locks the cache for writing to ensure thread safety, then inserts the item into the cache map.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s do Get, Delete, Len, you get the gist now
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Get an Item from store using its key&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Delete an item from store by its key&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Return the lenght of items in store&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, we’re almost done—I promise. The &lt;code&gt;Get&lt;/code&gt; function retrieves an item from the cache using its key. First, it locks the cache for reading and checks if the key exists in the &lt;code&gt;items&lt;/code&gt; map. If it doesn’t, it returns &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the item does exist, the function checks whether it has expired using the &lt;code&gt;Expired()&lt;/code&gt; method. If it has, it unlocks the cache and returns &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;. Otherwise, it returns the item’s value along with &lt;code&gt;true&lt;/code&gt; to indicate a successful cache hit.&lt;/p&gt;

&lt;p&gt;The next function is &lt;code&gt;Delete&lt;/code&gt;. This function removes an item from the cache by its key. It first locks the cache for reading and checks if the key exists in the &lt;code&gt;items&lt;/code&gt; map. If it doesn’t, it returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the key exists, it deletes the entry from the map and then unlocks the cache. Finally, it returns &lt;code&gt;true&lt;/code&gt; to indicate that the item was successfully removed.&lt;/p&gt;

&lt;p&gt;And finally &lt;code&gt;Len&lt;/code&gt;. This returns the length of items in the items map; nothing too fancy here.&lt;/p&gt;

&lt;h3&gt;
  
  
  But wait, there is one more thing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Delete all expired items from store&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;DeleteExpired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&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;Remember when I mentioned we have a &lt;strong&gt;Janitor&lt;/strong&gt;? The &lt;code&gt;DeleteExpired&lt;/code&gt; function is part of that cleanup process. It loops over all the items in the store and checks whether each one has expired. If an item is expired, it calls the &lt;code&gt;Delete&lt;/code&gt; function to remove it from the cache.&lt;/p&gt;

&lt;p&gt;This helps ensure the cache doesn't keep stale data and frees up memory for new entries, keeping the cache clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Janitor
&lt;/h3&gt;

&lt;p&gt;Now let’s talk about how the &lt;strong&gt;Janitor&lt;/strong&gt; runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Run janitor clean up after intervals&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Janitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteExpired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&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 &lt;code&gt;Run&lt;/code&gt; method starts a ticker that fires at intervals defined by the user. On each tick, it calls &lt;code&gt;DeleteExpired()&lt;/code&gt; on the cache — this is how we automatically clean up expired items without the user needing to call anything manually.&lt;/p&gt;

&lt;p&gt;We also listen for a stop signal through the &lt;code&gt;stop&lt;/code&gt; channel. Once that signal is received, we stop the ticker and exit the loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;stopExceution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;janitor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;startJanitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;janitor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Janitor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;janitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;janitor&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;janitor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&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;There’s also a &lt;code&gt;stopExecution&lt;/code&gt; function that sends the stop signal to the janitor. This gives us a way to &lt;strong&gt;&lt;em&gt;gracefully shut down&lt;/em&gt;&lt;/strong&gt; the cleanup process when the program is closing or we no longer need the cache.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;runJanitor&lt;/code&gt; function wires everything together. It creates a new Janitor with a cleanup interval, assigns it to the cache, and runs it in a goroutine so it works in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;defaultCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;runJanitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DefaultCacheInterval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetFinalizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stopExceution&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="n"&gt;defaultCache&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can run our cache and see our little in-memory cache in action&lt;/p&gt;

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

&lt;p&gt;This is a very small and maybe not production-ready cache, but I still remember the excitement I felt when it finally worked—and how it helped me with my little project (which I promise I did not abandon).&lt;/p&gt;

&lt;p&gt;In my next article, we’ll look at how to expand this further by adding &lt;strong&gt;LRU&lt;/strong&gt; support to the cache.&lt;/p&gt;

&lt;p&gt;Now remember: just because you &lt;em&gt;can&lt;/em&gt; build something yourself doesn’t always mean you &lt;em&gt;should&lt;/em&gt;. It’s often a smart move to use existing tools, especially when caching isn’t the core of your project.&lt;/p&gt;

&lt;p&gt;That said... I &lt;em&gt;do&lt;/em&gt; think you should try building your own &lt;a href="https://emperorsixpacks.hashnode.dev/how-to-implement-multi-device-authentication-system-with-fastapi-redis-and-jwt" rel="noopener noreferrer"&gt;multi-auth system&lt;/a&gt; at least once. Until next time ✌🏽&lt;/p&gt;

</description>
      <category>lru</category>
      <category>cache</category>
      <category>programming</category>
      <category>go</category>
    </item>
    <item>
      <title>x402: Pay-Per-Use Internet</title>
      <dc:creator>Andrew David</dc:creator>
      <pubDate>Thu, 27 Nov 2025 13:07:09 +0000</pubDate>
      <link>https://dev.to/emperorsixpacks/x402-pay-per-use-internet-4p30</link>
      <guid>https://dev.to/emperorsixpacks/x402-pay-per-use-internet-4p30</guid>
      <description>&lt;h2&gt;
  
  
  We All Hate Free Trials (Well, I Do)
&lt;/h2&gt;

&lt;p&gt;Sometimes I just want to test something new. Maybe it’s a new AI image tool, a premium API, pay for a digital content to a creator, some data service everyone won’t shut up about. But nope—the first thing I see is “Start your 7-day free trial!” Great… except now I have to hand over my card on day one. Then I spend the whole week stressing that I’ll forget to cancel and get slapped with a $99 charge for something I used only once.&lt;/p&gt;

&lt;p&gt;Even worse: sometimes I want my AI agent (ChatGPT, Claude, Grok) to use that service for me. Which means what—give my card to the AI? I’m reckless, but an AI agent is probably ten times more reckless than me.&lt;/p&gt;

&lt;p&gt;It’s dumb. We shouldn’t need credit cards just to test stuff. With x402, I load a single wallet once—with however much I feel like (even five bucks). From that moment on, me and any AI agent I control can tap into hundreds of services and pay only for what we actually use, down to the penny. No cards floating around. No surprise bills. No “don’t forget to cancel” alarms. I pay for what I use. My agent pays for what it uses. Everyone wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is x402 and Why?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Evolution of Web Payments — and Why x402 Matters
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;402 status code&lt;/strong&gt; has been around since the very beginning of HTTP. The idea was simple: if a resource needs payment before you can access it, the server would return a 402.&lt;/p&gt;

&lt;p&gt;The problem: nobody actually used it.&lt;br&gt;&lt;br&gt;
Not because the idea was bad, but because there was no standard way to &lt;em&gt;pay over HTTP&lt;/em&gt;. So developers defaulted to &lt;strong&gt;403 Unauthorized&lt;/strong&gt;, and everyone just built their own systems—subscriptions, one-time links, prepaid credits.&lt;/p&gt;

&lt;p&gt;These solutions work, but they all live &lt;em&gt;outside&lt;/em&gt; the actual web request. The big unanswered questions were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How do we handle &lt;strong&gt;payments inside a request&lt;/strong&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do we &lt;strong&gt;send payments over HTTP&lt;/strong&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Stablecoins Changed the Game
&lt;/h3&gt;

&lt;p&gt;Then stablecoins blew up. Between 2021 and 2025:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Supply jumped from &lt;strong&gt;under $30B&lt;/strong&gt; to &lt;strong&gt;over $280B&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On-chain volume hit &lt;strong&gt;$27 trillion&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Governments finally gave clear rules (EU’s MiCA, U.S. GENIUS Act, and similar laws)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stablecoins basically became the first form of money that’s fast, global, cheap, programmable, and actually works online. Perfect for internet payments.&lt;/p&gt;
&lt;h3&gt;
  
  
  x402: Payments Directly Through HTTP
&lt;/h3&gt;

&lt;p&gt;In May 2025, &lt;strong&gt;Coinbase introduced x402&lt;/strong&gt;, a simple idea that unlocks a lot of possibilities. It revives the old 402 status code and gives it a real purpose.&lt;/p&gt;

&lt;p&gt;With x402, if a resource requires payment, the server can return a &lt;strong&gt;402 Payment Required&lt;/strong&gt; response that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;how much you need to pay&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;where to send the payment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;optional details about what you're buying&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The client—your browser, app, or even an AI agent—can then pay instantly on chains like &lt;strong&gt;Solana, Base, or Polygon&lt;/strong&gt;, and the server delivers the requested content &lt;strong&gt;in the same connection&lt;/strong&gt;. No accounts, no signups Just pay-and-go.&lt;/p&gt;
&lt;h3&gt;
  
  
  Perfect Timing for AI Agents
&lt;/h3&gt;

&lt;p&gt;AI agents are exploding in popularity, and they constantly make requests, pull data, call APIs, and use computational resources. With x402:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Agents can &lt;strong&gt;pay for exactly what they use&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MCP servers can &lt;strong&gt;charge per request&lt;/strong&gt; automatically&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Digital systems can &lt;strong&gt;trade with each other&lt;/strong&gt; without any human setup&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows for more robust agentic systems and true agentic commerce&lt;/p&gt;
&lt;h3&gt;
  
  
  Microtransactions Finally Make Sense
&lt;/h3&gt;

&lt;p&gt;One of the best things about x402 is that tiny payments—stuff like a few cents or even fractions of a cent—actually become practical. With credit cards, fees make this impossible. But with fast crypto networks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pay &lt;strong&gt;1¢&lt;/strong&gt; to read an article&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay &lt;strong&gt;per API call&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay &lt;strong&gt;by the second&lt;/strong&gt; for compute&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay &lt;strong&gt;per kilobyte&lt;/strong&gt; of data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means companies can finally drop the free trial and allow test the product without having to worry about remembering to cancel another subscription.&lt;/p&gt;
&lt;h2&gt;
  
  
  So how does It work?
&lt;/h2&gt;

&lt;p&gt;At its core, x402 involves &lt;strong&gt;three main players&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Seller&lt;/strong&gt; – the server or service hosting the resource you want&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Buyer&lt;/strong&gt; – you (or your app, agent, or browser)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Facilitator&lt;/strong&gt; – the service that verifies signatures and settles payments on-chain&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1. The Buyer
&lt;/h3&gt;

&lt;p&gt;You (the buyer) send a regular GET or POST request to the server, but with one extra header:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;X-Wallet&lt;/code&gt; header, which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;your wallet address&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the payment methods/networks your client supports&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This basically tells the server:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;“Hey, I have a wallet. If this costs money, I’m ready.”&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The Seller
&lt;/h3&gt;

&lt;p&gt;If the resource is paywalled, the server returns a &lt;strong&gt;402 Payment Required&lt;/strong&gt; response containing an invoice.&lt;/p&gt;

&lt;p&gt;Example:&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;"maxAmountRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/video/generate"&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;"Access requires payment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xABCDEF1234567390ABCDEF1234567890ABCDEF12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xA0b86991C6218236c1d19D4a2e9Eb0cE3606EB48"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base-mainnet"&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 tells the buyer exactly what needs to be paid, in what currency, and where.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Gasless payments using EIP-3008
&lt;/h3&gt;

&lt;p&gt;x402 uses &lt;a href="https://eips.ethereum.org/EIPS/eip-3009" rel="noopener noreferrer"&gt;&lt;strong&gt;EIP-3008: TransferWithAuthorization&lt;/strong&gt;&lt;/a&gt; to enable &lt;em&gt;gasless&lt;/em&gt; transfers.&lt;/p&gt;

&lt;p&gt;Instead of sending a transaction on-chain, the buyer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Signs a payload authorizing the payment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sends that signed payload back to the seller in an &lt;code&gt;X-Payment&lt;/code&gt; header&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&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="err"&gt;X-Payment:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;signed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;EIP&lt;/span&gt;&lt;span class="mi"&gt;-3008&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payload&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No gas. Just a signature.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The Seller forwards the signature to the Facilitator
&lt;/h3&gt;

&lt;p&gt;The seller never posts anything directly on-chain.&lt;/p&gt;

&lt;p&gt;Instead, it forwards the signed payment to the facilitator, who:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;verifies it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;settles the payment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;confirms everything succeeded&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once payment is settled, the seller returns the requested resource—&lt;strong&gt;all in the same request flow&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not API Keys?
&lt;/h2&gt;

&lt;p&gt;x420 shines by allowing users to make transactions &lt;strong&gt;without needing API keys&lt;/strong&gt; or even accounts on the services they interact with. This means a single wallet can grant access to premium services across multiple platforms.&lt;/p&gt;

&lt;p&gt;The major advantage is that &lt;strong&gt;AI agents can independently compose and pay for API requests&lt;/strong&gt; without any user intervention. Imagine you have an AI agent that needs to publish posts every day based on what’s trending in the country. Your agent will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A search tool to find what’s currently trending&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access to Twitter/X to gather up-to-date news and topics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The ability to publish a post on your Twitter/X account&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normally, this requires API credits and API keys for platforms like Google and Twitter. While manageable for a small project, the number of API keys quickly becomes overwhelming as your system grows and you integrate more services.&lt;/p&gt;

&lt;p&gt;With x420, &lt;strong&gt;one wallet covers the cost of all transactions&lt;/strong&gt;, regardless of the number of services involved. All you need to manage is the balance in that single wallet. And you can even set spend limits so your AI agent doese’t spend above a certain threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now The problem
&lt;/h2&gt;

&lt;p&gt;x402 is impressive—&lt;em&gt;really&lt;/em&gt; impressive—but it still carries challenges that are common across the entire crypto ecosystem. The most significant concern is that crypto transactions generally &lt;strong&gt;cannot be traced back to a specific individual or organization&lt;/strong&gt;. While this privacy is a feature, it also creates the potential for x402 to be misused for money laundering or online fraud. These concerns are not unique to x402; they apply broadly to most blockchain-based systems and projects.&lt;/p&gt;

&lt;p&gt;Another important issue currently being worked on is transparency. Even though the origin of an agent’s transaction may be private to the service provider, the &lt;strong&gt;transaction itself is still publicly visible on the blockchain&lt;/strong&gt;. This means that, despite appearing anonymous, your agent’s actions can still be tracked, and most users do not want their agent’s financial activity to be publicly exposed.&lt;/p&gt;

&lt;p&gt;Addressing this would require &lt;a href="https://x.com/PRXVTai/status/1991962982155448347?s=20" rel="noopener noreferrer"&gt;&lt;strong&gt;privacy-preserving transactions&lt;/strong&gt;&lt;/a&gt;, along with &lt;strong&gt;selective disclosure&lt;/strong&gt;—allowing only authorized parties (such as auditors or regulators) to view transaction details when necessary.&lt;/p&gt;

&lt;p&gt;Another concern with x402 is the &lt;strong&gt;lack of chargebacks or refunds&lt;/strong&gt;. Because transactions are irreversible, victims these of scams cannot recover lost funds. This raises the risk of an increase in crypto scams.&lt;/p&gt;

&lt;p&gt;One proposed solution is the introduction of &lt;strong&gt;reputation systems&lt;/strong&gt;. Services can be rated by users and agents, and poor ratings reduce the likelihood that others will trust or interact with them. While this improves accountability, it does not fully solve the chargeback problem, as victims still cannot retrieve their money.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conclusion
&lt;/h2&gt;

&lt;p&gt;So yeah—that’s x402. This article isn’t meant to be highly technical, but it gives you a broad overview of what x402 is and its potential impact on online payments. One user even described it as &lt;em&gt;“the next big thing after sliced bread.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Innovation around x402 is moving fast, and &lt;strong&gt;version 2 is already on the way&lt;/strong&gt; with even more improvements. One of the most exciting aspects of x402 is that it’s simply a &lt;strong&gt;set of rules for moving money over HTTP&lt;/strong&gt;. Although crypto is currently being used as the payment layer, the standard itself is not limited to crypto. This means x402 has the potential to move even &lt;strong&gt;fiat currency over HTTP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Experiments have already been conducted using services like Stripe—not yet public, but promising—which shows just how far-reaching the potential of x402 could be. The future of online payments built on this standard is genuinely exciting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.reuters.com/business/finance/stablecoins-market-cap-surges-record-high-us-senate-passes-bill-2025-06-18/" rel="noopener noreferrer"&gt;Stablecoins' market cap surges to record high as US senate passes bill&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.x402.org/" rel="noopener noreferrer"&gt;x402.org&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://x402.gitbook.io/x402" rel="noopener noreferrer"&gt;https://x402.gitbook.io/x402&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.quicknode.com/guides/infrastructure/how-to-use-x402-payment-required" rel="noopener noreferrer"&gt;How to Implement a Crypto Paywall with x402 Payment Protocol&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://open.spotify.com/episode/40zesAep8MlNYiPBbiXpXX?si=ec267f921de64f23" rel="noopener noreferrer"&gt;How the x402 Standard is Enabling AI Agents to Pay Each Other -Ep. 948&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://open.spotify.com/episode/6QLYIyrCEig3zUfD56twLn?si=b4e9146e67404f75" rel="noopener noreferrer"&gt;x402: The Key to Internet Money, Micropayments &amp;amp; The AI Economy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=XL_Nn4oep6M&amp;amp;t=23498s" rel="noopener noreferrer"&gt;Devconnect ARG - M2 Yellow Pavilion&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>x402</category>
      <category>ai</category>
      <category>blockchain</category>
      <category>payments</category>
    </item>
  </channel>
</rss>
