<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ caching - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ caching - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 05:06:00 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/caching/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Optimize Django REST APIs for Performance: Profiling, Caching, and Scaling. ]]>
                </title>
                <description>
                    <![CDATA[ Performance problems in APIs rarely start as performance problems. They usually start as small design decisions that worked perfectly when the application had ten users, ten records, or a single developer testing locally. Over time, as traffic increa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-optimize-django-rest-apis-for-performance/</link>
                <guid isPermaLink="false">6994b1d13e0696149c7c229c</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ django rest framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Performance Optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mari ]]>
                </dc:creator>
                <pubDate>Tue, 17 Feb 2026 18:22:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771352481135/11be538b-aaf5-4c1e-8ee2-99deea5f180e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Performance problems in APIs rarely start as performance problems. They usually start as small design decisions that worked perfectly when the application had ten users, ten records, or a single developer testing locally. Over time, as traffic increases and data grows, those same decisions begin to slow everything down.</p>
<p>In this article, we’ll walk step by step through how performance issues arise in Django REST APIs, how to see them clearly using profiling tools, and how to fix them using query optimization, caching, pagination, and basic scaling strategies.</p>
<p>This article will be most useful for developers who already understand Django, the Django REST Framework, and REST concepts, but are new to performance optimization.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-django-rest-apis-become-slow">Why Django REST APIs Become Slow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-profiling-finding-the-real-bottlenecks">Profiling: Finding the Real Bottlenecks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-logging-sql-queries">Logging SQL Queries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary-and-next-steps">Summary and Next Steps</a></p>
</li>
</ul>
<h2 id="heading-why-django-rest-apis-become-slow">Why Django REST APIs Become Slow</h2>
<p>Before optimizing anything, it’s important to understand why APIs become slow in the first place.</p>
<p>Most performance issues in Django REST APIs come from three main sources:</p>
<ol>
<li><p>Too many database queries</p>
</li>
<li><p>Doing expensive work repeatedly</p>
</li>
<li><p>Returning more data than necessary</p>
</li>
</ol>
<p>Django is fast by default, but it does exactly what you ask it to do. If your API endpoint triggers 300 database queries, Django will happily run all 300.</p>
<p>Now let’s look at some common causes of performance issues in Django REST APIs.</p>
<h3 id="heading-1-n1-query-problems-in-serializers">1. N+1 Query Problems in Serializers</h3>
<p>This happens when you loop over objects and access related fields, causing a separate query for each object.</p>
<pre><code class="lang-python"><span class="hljs-comment"># models.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

<span class="hljs-comment"># views.py (naive approach)</span>
posts = Post.objects.all()
<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-comment"># This triggers a query per post to fetch the author</span>
    print(post.author.name)
</code></pre>
<p>If you have 100 posts, this runs 101 queries: 1 for posts and 100 for authors. Django lazily loads related objects by default, so without intervention, your API performs repetitive database work that slows response times.</p>
<h3 id="heading-2-fetching-related-objects-inefficiently">2. Fetching Related Objects Inefficiently</h3>
<pre><code class="lang-python"><span class="hljs-comment"># Naive queryset fetching all related objects separately</span>
posts = Post.objects.all()
authors = [post.author <span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts]  <span class="hljs-comment"># triggers extra queries per post</span>
</code></pre>
<p>Each access to <code>post.author</code> triggers a new query. Even though you already fetched all posts, Django lazily loads related objects by default. This creates many extra queries, slowing down your API.</p>
<h3 id="heading-3-serializing-large-datasets-without-pagination">3. Serializing Large Datasets Without Pagination</h3>
<p>Returning large query sets all at once can slow down your API and increase memory usage.</p>
<pre><code class="lang-python"><span class="hljs-comment"># views.py</span>
<span class="hljs-keyword">from</span> rest_framework.response <span class="hljs-keyword">import</span> Response
<span class="hljs-keyword">from</span> rest_framework.decorators <span class="hljs-keyword">import</span> api_view
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post
<span class="hljs-keyword">from</span> .serializers <span class="hljs-keyword">import</span> PostSerializer

<span class="hljs-meta">@api_view(['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">all_posts</span>(<span class="hljs-params">request</span>):</span>
    posts = Post.objects.all()  <span class="hljs-comment"># retrieves all posts at once</span>
    serializer = PostSerializer(posts, many=<span class="hljs-literal">True</span>)
    <span class="hljs-keyword">return</span> Response(serializer.data)
</code></pre>
<p>If your database has thousands of posts, this endpoint fetches everything in memory, serializes it, and sends it over the network. It’s slow and can crash under load. Later, we’ll learn to paginate results efficiently.</p>
<h3 id="heading-4-recomputing-expensive-work-repeatedly">4. Recomputing Expensive Work Repeatedly</h3>
<p>Some endpoints calculate the same values on every request instead of caching or precomputing.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">expensive_view</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-comment"># Simulate expensive computation</span>
    result = sum([i**<span class="hljs-number">2</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000000</span>)])
    <span class="hljs-keyword">return</span> JsonResponse({<span class="hljs-string">"result"</span>: result})
</code></pre>
<p>Even if the data doesn’t change often, this computation happens on every request, consuming CPU time unnecessarily.  </p>
<p>Performance optimization is about reducing unnecessary work.  </p>
<p>At this point, it might be tempting to jump straight into fixes like caching responses or optimizing database queries. But doing that without evidence often leads to wasted effort or even new problems.</p>
<p>Before changing anything, you need to understand where your API is actually spending time. Is it the database? Is it serialization? Is it Python code running repeatedly on every request? This is where profiling becomes essential.</p>
<h2 id="heading-profiling-finding-the-real-bottlenecks">Profiling: Finding the Real Bottlenecks</h2>
<p>Optimizing without profiling is guessing. Profiling helps you answer one question:</p>
<blockquote>
<p>Where is my API actually spending time?</p>
</blockquote>
<p>In practice, profiling means observing an API while it runs and collecting data about what it’s doing. This includes how many database queries are executed, how long those queries take, and how much time is spent in Python code, such as serializers or business logic.</p>
<p>By profiling first, you avoid making assumptions and can focus on fixing the parts of your API that are truly slowing things down.</p>
<h3 id="heading-measuring-query-count-in-a-view">Measuring Query Count in a View</h3>
<p>During development, Django keeps track of all executed queries. You can inspect them directly:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> connection
<span class="hljs-keyword">from</span> rest_framework.decorators <span class="hljs-keyword">import</span> api_view
<span class="hljs-keyword">from</span> rest_framework.response <span class="hljs-keyword">import</span> Response
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post
<span class="hljs-keyword">from</span> .serializers <span class="hljs-keyword">import</span> PostSerializer

<span class="hljs-meta">@api_view(["GET"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post_list</span>(<span class="hljs-params">request</span>):</span>
    posts = Post.objects.all()
    serializer = PostSerializer(posts, many=<span class="hljs-literal">True</span>)

    response = Response(serializer.data)

    print(<span class="hljs-string">f"Total queries executed: <span class="hljs-subst">{len(connection.queries)}</span>"</span>)

    <span class="hljs-keyword">return</span> response
</code></pre>
<p>If this prints 101 queries for 100 posts, you likely have an N+1 problem. This simple check confirms whether the database layer is the bottleneck.</p>
<p>One of the easiest ways to profile Django applications during development is by using tools that expose this information directly while requests are being processed.</p>
<h3 id="heading-using-the-django-debug-toolbar">Using the Django Debug Toolbar</h3>
<p>The Django Debug Toolbar is one of the simplest ways to understand performance during development. It acts as a lightweight profiling tool that shows what happens behind the scenes when a request is handled.</p>
<p>It shows you:</p>
<ul>
<li><p>How many SQL queries were executed</p>
</li>
<li><p>How long each query took</p>
</li>
<li><p>whether queries are duplicated</p>
</li>
<li><p>Which parts of the request lifecycle are slow</p>
</li>
</ul>
<h4 id="heading-how-to-install-and-enable-the-django-debug-toolbar">How to Install and Enable the Django Debug Toolbar</h4>
<p>First, install it:</p>
<pre><code class="lang-bash">pip install django-debug-toolbar
</code></pre>
<p>In settings.py:</p>
<pre><code class="lang-bash">INSTALLED_APPS = [
    ...
    <span class="hljs-string">"debug_toolbar"</span>,
]

MIDDLEWARE = [
    ...
    <span class="hljs-string">"debug_toolbar.middleware.DebugToolbarMiddleware"</span>,
]

INTERNAL_IPS = [
    <span class="hljs-string">"127.0.0.1"</span>,
]
</code></pre>
<p>In urls.py:</p>
<pre><code class="lang-bash">import debug_toolbar
from django.urls import path, include

urlpatterns = [
    ...
    path(<span class="hljs-string">"__debug__/"</span>, include(debug_toolbar.urls)),
]
</code></pre>
<p>When you load an endpoint in the browser during development, the toolbar displays total SQL queries, execution time, and duplicate queries. This makes inefficiencies immediately visible.</p>
<p>When you load an API endpoint and see 150 SQL queries for a single request, that’s a strong signal that something is wrong, often an N+1 query problem or inefficient serializer behavior.</p>
<h3 id="heading-logging-sql-queries">Logging SQL Queries</h3>
<p>Django allows you to log all executed SQL queries. This is especially useful when debugging API views.</p>
<p>Seeing the raw SQL makes inefficiencies obvious, such as repeated <code>SELECT</code> statements for the same table.</p>
<h4 id="heading-how-to-enable-sql-query-logging">How to Enable SQL Query Logging</h4>
<p>You can configure Django to log all SQL queries in settings.py:</p>
<pre><code class="lang-bash">LOGGING = {
    <span class="hljs-string">"version"</span>: 1,
    <span class="hljs-string">"handlers"</span>: {
        <span class="hljs-string">"console"</span>: {
            <span class="hljs-string">"class"</span>: <span class="hljs-string">"logging.StreamHandler"</span>,
        },
    },
    <span class="hljs-string">"loggers"</span>: {
        <span class="hljs-string">"django.db.backends"</span>: {
            <span class="hljs-string">"handlers"</span>: [<span class="hljs-string">"console"</span>],
            <span class="hljs-string">"level"</span>: <span class="hljs-string">"DEBUG"</span>,
        },
    },
}
</code></pre>
<p>With this configuration, every SQL query will be printed to the console when your API runs. Repeated SELECT statements or unexpected queries become obvious.</p>
<h3 id="heading-profiling-api-response-time">Profiling API Response Time</h3>
<p>Database queries are only one part of API performance. Beyond queries, it’s also important to measure the total response time of an endpoint.</p>
<p>Profiling response time helps you understand whether delays are caused by database access or by other parts of the request lifecycle. For example, if an endpoint takes 1.2 seconds to respond but only 50 milliseconds are spent on database queries, the bottleneck is likely in serialization, business logic, or repeated computations in Python.</p>
<p>By comparing query time and total response time, profiling helps you identify what to fix first instead of optimizing the wrong layer of the system.</p>
<h4 id="heading-how-to-measure-total-response-time">How to Measure Total Response Time</h4>
<pre><code class="lang-bash">import time
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view([<span class="hljs-string">"GET"</span>])
def example_view(request):
    start_time = time.time()

    <span class="hljs-comment"># Simulate work</span>
    data = {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world"</span>}

    response = Response(data)

    end_time = time.time()
    <span class="hljs-built_in">print</span>(f<span class="hljs-string">"Response time: {end_time - start_time:.4f} seconds"</span>)

    <span class="hljs-built_in">return</span> response
</code></pre>
<p>If database queries are fast but the total response time is high, the bottleneck may be serialization or expensive Python logic.  </p>
<p>Once you’ve identified that database access is a significant contributor to slow response times, the next step is to look more closely at how Django retrieves related data.</p>
<h3 id="heading-sql-query-optimization-in-django-rest-apis">SQL Query Optimization in Django REST APIs</h3>
<p>One of the most common reasons Django REST APIs become slow is inefficient access to related objects. This often manifests as the N+1 query problem, where fetching related objects triggers a separate database query for each item. Identifying and fixing this problem can significantly reduce the number of queries and improve API performance.</p>
<h4 id="heading-understanding-the-n1-query-problem">Understanding the N+1 Query Problem</h4>
<p>Consider a simple example:</p>
<ul>
<li><p>You fetch a list of posts</p>
</li>
<li><p>Each post has an author</p>
</li>
<li><p>For every post, Django fetches the author separately</p>
</li>
</ul>
<p>If you have 100 posts, this results in 101 queries: 1 for the posts and 100 for the authors. This happens because Django lazily loads related objects by default. Without intervention, your API performs repetitive database work that slows down response times.</p>
<h4 id="heading-solving-the-problem-with-selectrelated-and-prefetchrelated">Solving the Problem with <code>select_related</code> and <code>prefetch_related</code></h4>
<p>Django provides built-in tools to control how related objects are loaded efficiently: <code>select_related</code> and <code>prefetch_related</code>.</p>
<p><strong>1. Using</strong> <code>select_related</code></p>
<p><code>select_related</code> is designed for foreign key and one-to-one relationships. It performs an SQL join and retrieves related objects in a single query.</p>
<p>Use it when:</p>
<ul>
<li><p>You know you will access related objects</p>
</li>
<li><p>The relationship is one-to-one or many-to-one</p>
</li>
</ul>
<pre><code class="lang-bash">posts = Post.objects.select_related(<span class="hljs-string">"author"</span>)

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.author.name)  <span class="hljs-comment"># No additional queries</span>
</code></pre>
<p>This performs a SQL JOIN and retrieves posts and authors in a single query, eliminating the N+1 problem.</p>
<p>It reduces multiple queries into just one, avoiding repeated database hits.</p>
<p><strong>2. Using</strong> <code>prefetch_related</code></p>
<p><code>prefetch_related</code> is used for many-to-many and reverse foreign key relationships. It performs separate queries for each related table but combines the results in Python.</p>
<p>Use it when:</p>
<ul>
<li><p>A SQL join would produce too much duplicated data</p>
</li>
<li><p>You are dealing with collections of related objects</p>
</li>
</ul>
<h4 id="heading-example-how-to-optimize-a-many-to-many-relationship">Example: How to Optimize a Many-to-Many Relationship</h4>
<p>Consider a blog application where posts can have multiple tags:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># models.py</span>
class Tag(models.Model):
    name = models.CharField(max_length=50)

class Post(models.Model):
    title = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag)
</code></pre>
<p>Now imagine fetching posts and accessing their tags:</p>
<pre><code class="lang-bash">posts = Post.objects.all()

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.tags.all())  <span class="hljs-comment"># Triggers additional queries</span>
</code></pre>
<p>If you have 100 posts, Django may execute:</p>
<ul>
<li><p>1 query to fetch posts</p>
</li>
<li><p>1 query per post to fetch related tags</p>
</li>
</ul>
<p>This results in many unnecessary database hits.</p>
<p>You can optimize this using <code>prefetch_related</code>:</p>
<pre><code class="lang-bash">posts = Post.objects.prefetch_related(<span class="hljs-string">"tags"</span>)

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.tags.all())  <span class="hljs-comment"># Uses prefetched data</span>
</code></pre>
<p>With this approach, Django performs one query for posts and one query for all related tags. It then matches them in Python, eliminating repeated database queries.</p>
<p>Together, these tools allow you to optimize your queries and eliminate the N+1 problem efficiently.</p>
<h4 id="heading-common-beginner-mistakes">Common Beginner Mistakes</h4>
<p>Even after applying these optimizations, it’s easy to make mistakes. Watch out for:</p>
<ul>
<li><p>Forgetting that serializers can trigger additional queries</p>
</li>
<li><p>Using <code>select_related</code> on many-to-many relationships</p>
</li>
<li><p>Assuming Django automatically optimizes queries</p>
</li>
<li><p>Not checking the query count after adding serializers</p>
</li>
</ul>
<p>Paying attention to these pitfalls ensures your API remains fast and scalable.</p>
<h3 id="heading-caching-in-django-rest-apis">Caching in Django REST APIs</h3>
<p>Even after optimizing database queries, API performance can still suffer if the same computations or database lookups are performed repeatedly. This is where caching comes in. Caching is a technique for storing the results of expensive operations so they can be retrieved more quickly the next time they are needed.</p>
<p>At its core, caching exists because computers have multiple layers of memory with different speeds:</p>
<ul>
<li><p>CPU registers (fastest)</p>
</li>
<li><p>L1, L2, L3 caches</p>
</li>
<li><p>Main memory (RAM)</p>
</li>
<li><p>SSD storage</p>
</li>
<li><p>HDD storage (slowest)</p>
</li>
</ul>
<p>Each layer trades speed for size: the closer the data is to the CPU, the faster it can be accessed. Software systems use the same principle; by storing frequently accessed data in a “closer” or faster location, applications can respond more quickly.</p>
<h4 id="heading-cache-eviction">Cache Eviction</h4>
<p>Caches are limited in size, so when a cache is full, some data must be removed to make room for new data. This process is called cache eviction.</p>
<p>Common eviction strategies include:</p>
<ul>
<li><p><strong>Least Recently Used (LRU):</strong> removes the data that hasn’t been accessed for the longest time</p>
</li>
<li><p><strong>Random Replacement:</strong> removes a random item from the cache</p>
</li>
</ul>
<p>The goal is to keep the data that is most likely to be requested again while freeing space for new data. Understanding this helps developers use caching effectively.</p>
<h4 id="heading-caching-in-application-architectures">Caching in Application Architectures</h4>
<p>Caching exists at several levels in modern software systems:</p>
<ul>
<li><p><strong>Client-side caching:</strong> Web browsers cache HTTP responses to reduce the need for repeated network requests. This is controlled with HTTP headers like <code>Cache-Control</code>.</p>
</li>
<li><p><strong>CDN caching:</strong> Content Delivery Networks store static assets closer to users, reducing latency and server load.</p>
</li>
<li><p><strong>Backend caching:</strong> Backend services cache results from database queries, computed values, or API responses. This is where Django caching is most commonly applied.</p>
</li>
</ul>
<p>By applying caching strategically at the backend, APIs can serve data faster while reducing computation and database load.</p>
<h4 id="heading-caching-in-django">Caching in Django</h4>
<p>Django provides a flexible caching framework that supports multiple backends, including in-memory, file-based, database-backed, and third-party stores like Redis. The main types of caching in Django are:</p>
<ol>
<li><p><strong>Per-view caching:</strong> caches the entire output of a view. Ideal for endpoints where responses rarely change.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">from</span> django.views.decorators.cache <span class="hljs-keyword">import</span> cache_page

<span class="hljs-meta"> @cache_page(60 * 15)  # cache for 15 minutes</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_view</span>(<span class="hljs-params">request</span>):</span>
</code></pre>
<ol start="2">
<li><p>Template fragment caching: caches specific parts of a template to avoid repeated rendering.</p>
</li>
<li><p>Low-level caching: gives full control over what is cached and for how long, making it ideal for API responses.</p>
</li>
</ol>
</li>
</ol>
<p>    By combining these approaches, you can reduce repeated work in your API, lower database load, and speed up response times.</p>
<h3 id="heading-when-to-use-redis">When to Use Redis</h3>
<p>While Django’s built-in caching backends are sufficient for many projects, high-traffic APIs often require a shared, in-memory cache. This is where Redis excels. Redis is designed for fast access, low latency, and can handle frequent reads across multiple servers.</p>
<p>You should consider using Redis when:</p>
<ul>
<li><p>Data is read frequently but changes infrequently</p>
</li>
<li><p>Low latency is important for API responses</p>
</li>
<li><p>You need cache expiration and eviction policies</p>
</li>
<li><p>You want a shared cache across multiple servers or services</p>
</li>
</ul>
<p>Redis is particularly effective for API endpoints that serve the same data to many users, such as frequently accessed lists or computed results.</p>
<h3 id="heading-common-beginner-mistakes-1">Common Beginner Mistakes</h3>
<p>Caching is powerful, but it’s easy to misuse. Some common pitfalls include:</p>
<ul>
<li><p><strong>Caching everything blindly:</strong> not all data benefits from caching</p>
</li>
<li><p><strong>Forgetting cache invalidation:</strong> stale data can lead to incorrect responses</p>
</li>
<li><p><strong>Using cache where query optimization would suffice:</strong> sometimes optimizing database queries is a better solution than caching.</p>
</li>
</ul>
<p>Remember: caching should complement good database design, not replace it.</p>
<h3 id="heading-pagination-and-limiting-expensive-datasets">Pagination and Limiting Expensive Datasets</h3>
<p>Even with caching, returning large datasets in a single request can slow down your API and increase memory usage. Pagination is a simple and effective way to limit the amount of data returned at once.</p>
<p>Pagination helps by reducing:</p>
<ul>
<li><p>Database load</p>
</li>
<li><p>Memory usage</p>
</li>
<li><p>Serialization time</p>
</li>
<li><p>Network transfer size</p>
</li>
</ul>
<p>Django REST Framework provides built-in pagination classes that make it easy to paginate endpoints. As a rule of thumb, always paginate list endpoints unless there is a strong reason not to.</p>
<h3 id="heading-load-testing-and-measuring-improvement">Load Testing and Measuring Improvement</h3>
<p>Optimizations are only meaningful if you can measure their impact. Load testing simulates multiple users accessing your API simultaneously, helping you answer key questions:</p>
<ul>
<li><p>How many requests per second can my API handle?</p>
</li>
<li><p>Where does the API start to break under load?</p>
</li>
<li><p>Did caching, query optimization, and pagination actually improve performance?</p>
</li>
</ul>
<p>By running load tests before and after optimization, you can validate that your changes have the desired effect and avoid optimizing the wrong parts of your system.</p>
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<p>Optimizing Django REST APIs isn’t about chasing every tiny micro-optimization. It’s about reducing unnecessary work and focusing on the parts of your API that actually slow down performance.</p>
<h4 id="heading-key-takeaways">Key Takeaways</h4>
<ul>
<li><p><strong>Profile before optimizing:</strong> Identify the real bottlenecks before making changes.</p>
</li>
<li><p><strong>Reduce database queries:</strong> Use techniques like <code>select_related</code>, <code>prefetch_related</code>, and avoid N+1 queries.</p>
</li>
<li><p><strong>Cache frequently accessed data:</strong> Use Django caching and Redis to reduce repeated computations.</p>
</li>
<li><p><strong>Paginate large datasets:</strong> Limit memory usage and network load by returning data in chunks.</p>
</li>
<li><p><strong>Measure performance changes:</strong> Always verify that your optimizations have a real impact.</p>
</li>
</ul>
<h4 id="heading-next-steps-for-your-apis">Next Steps for Your APIs</h4>
<ol>
<li><p><strong>Add profiling to your existing APIs</strong> to understand where time is spent.</p>
</li>
<li><p><strong>Identify one slow endpoint</strong> and focus on optimizing it first.</p>
</li>
<li><p><strong>Optimize database queries</strong> using Django ORM best practices.</p>
</li>
<li><p><strong>Introduce caching carefully</strong>; avoid caching everything blindly.</p>
</li>
<li><p><strong>Measure the results</strong> with load testing and performance metrics.</p>
</li>
</ol>
<p>Remember: Performance optimization is not a one-time task. It’s a habit built by continuously observing how your system works, testing improvements, and applying changes where they make the most impact.</p>
<h2 id="heading-read-more">Read More</h2>
<ol>
<li><p><a target="_blank" href="https://www.django-rest-framework.org/topics/performance/">DRF Performance</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/optimization/">Django ORM Optimization</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/optimization/#select-related">Understanding N+1 queries</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why Your UI Won’t Update: Debugging Stale Data and Caching in React Apps ]]>
                </title>
                <description>
                    <![CDATA[ Your UI doesn’t “randomly” refuse to update. In most cases, it’s rendering cached data, which is data that was saved somewhere so the app doesn’t have to do the same work again. Caching is great for performance, but it becomes a pain when you don’t r... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/why-your-ui-wont-update-debugging-stale-data-and-caching-in-react-apps/</link>
                <guid isPermaLink="false">6984d41160b1e5f9aeccaa9e</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwadamisi Samuel ]]>
                </dc:creator>
                <pubDate>Thu, 05 Feb 2026 17:32:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770312709391/8442f6df-1133-47f7-a035-02c958145811.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Your UI doesn’t “randomly” refuse to update. In most cases, it’s rendering cached data, which is data that was saved somewhere so the app doesn’t have to do the same work again.</p>
<p>Caching is great for performance, but it becomes a pain when you don’t realize which layer is reusing old data.</p>
<p>If you’ve ever seen this:</p>
<ul>
<li><p>You update a profile name, but the screen still shows the old one.</p>
</li>
<li><p>You delete an item, but it stays in the list.</p>
</li>
<li><p>Your API returns fresh JSON, but the page refuses to change.</p>
</li>
<li><p>You deploy a fix, but your teammate still sees the old behavior.</p>
</li>
</ul>
<p>You’re probably hitting a cache.</p>
<p>What makes this especially confusing is that not all stale UI comes from “real” caches. Modern web apps have multiple places where data can be reused, saved, or replayed between your UI, your API and when your app is deployed. When you don’t have a clear mental model of these layers, debugging turns into guesswork.</p>
<p>This article lays out a practical guide of the five most common caching layers that cause stale UI, plus one non-cache trap that looks exactly like one. The goal is to help you quickly identify where stale data is coming from, so you can fix the right thing instead of “refreshing harder.”</p>
<h2 id="heading-why-it-matters">Why it Matters</h2>
<p>I first ran into this while building an app where the UI wouldn’t update after a successful change. The API returned 200 OK, the database was correct, but the screen stayed stale. I assumed something was wrong with my code or state logic. Instead, the issue was coming from a caching layer I hadn’t invalidated. That’s the real problem with stale UI, you can’t debug it effectively unless you know which layer might be serving cached data.</p>
<p>When you understand where caching happens:</p>
<ul>
<li><p>You debug faster by identifying the layer instead of guessing.</p>
</li>
<li><p>You avoid production-only bugs caused by caching defaults.</p>
</li>
<li><p>You stop chasing React issues when the data was never fresh.</p>
</li>
</ul>
<p>This article gives you a simple mental model to pinpoint the layer and fix the right thing.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-it-matters">Why it Matters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-mental-model">The Mental Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-non-cache-cause">Non-Cache Cause</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cache-1-react-query-cache">Cache 1: React Query Cache</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cache-2-nextjs-fetch-caching">Cache 2: Next.js fetch() Caching</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cache-3-browser-http-cache-a-saved-copy-in-your-browser">Cache 3: Browser HTTP Cache (a Saved Copy in Your Browser)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cache-4-cdnhosting-cache">Cache 4: CDN/Hosting Cache</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cache-5-service-worker-cache-only-if-your-site-is-a-pwa">Cache 5: Service Worker Cache (Only if Your Site is a PWA)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-10-second-debug-guide">10-Second Debug Guide</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prevention-set-caching-intentionally">Prevention: Set Caching Intentionally</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-recap">Recap</a></p>
</li>
</ul>
<h2 id="heading-the-mental-model">The Mental Model</h2>
<p>When your UI shows data, it feels like it comes straight from your API. In reality, the request/response path can hit multiple reuse points.</p>
<h2 id="heading-non-cache-cause">Non-Cache Cause</h2>
<p>Duplicated React local state (same symptoms as caching). This one isn’t a formal cache, but it causes a lot of “why didn’t it update?” bugs especially for beginners.</p>
<h3 id="heading-the-common-trap">The common trap:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [name, setName] = useState(user.name) <span class="hljs-comment">// initialized once</span>
</code></pre>
<p><code>useState</code> only uses its argument during the initial render. On every subsequent render, React ignores this value and preserves the existing state.</p>
<p>If <code>user.name</code> later changes (for example, after fresh API data arrives), the <code>name</code> state will not update automatically. At that point, <code>name</code> becomes a stale copy of <code>user.name</code>, and the UI renders outdated data unless you manually synchronize it.</p>
<p>This happens because you have duplicated state:</p>
<ul>
<li><p><code>user.name</code> is the source of truth.</p>
</li>
<li><p><code>name</code> state is a local snapshot taken once.</p>
</li>
</ul>
<p>React does not keep duplicated state in sync for you.</p>
<p>Correct patterns:</p>
<ol>
<li>Render directly from the source when possible.</li>
</ol>
<p>If the value is not being edited locally, do not copy it into state:</p>
<pre><code class="lang-javascript">&lt;span&gt;{user.name}&lt;/span&gt;
</code></pre>
<p>This guarantees the UI always reflects the latest data.</p>
<ol start="2">
<li>Explicitly synchronize local state when editable state is required.</li>
</ol>
<p>If you need local, editable state (for example, a controlled input), you must opt in to synchronization:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [name, setName] = useState(user.name);  

    useEffect(<span class="hljs-function">() =&gt;</span> {    
        setName(user.name); 
     }, [user.name]);
</code></pre>
<p>This effect runs only when <code>user.name</code> changes, explicitly updating local state to match the new source value.</p>
<h2 id="heading-cache-1-react-query-cache">Cache 1: React Query Cache</h2>
<p>React Query (TanStack Query) stores query results in a QueryClient cache (in memory by default) so your UI can render quickly and avoid unnecessary network requests. When a component needs data, React Query can return cached data immediately and then decide whether to fetch the data again based on options like <code>staleTime</code> and “refetch” behaviors (on mount, window focus, reconnect).</p>
<h3 id="heading-common-failure-mode-mutation-succeeds-but-the-ui-stays-old">Common failure mode: mutation succeeds, but the UI stays old</h3>
<p>A 200 OK only confirms the mutation request succeeded. It does not automatically update the cached query data your UI is rendering.</p>
<p>After a mutation, one of these usually happens:</p>
<ul>
<li><p>The query that renders the screen was not invalidated/fetched</p>
</li>
<li><p>You invalidated the wrong query key (the UI reads from a different key)</p>
</li>
<li><p>The UI is rendering local React state that’s out of sync (not the query result)</p>
</li>
</ul>
<p>The simplest “safe” pattern is: invalidate the exact query key your UI uses, so it fetches fresh data.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useMutation, useQueryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tanstack/react-query"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useUpdateProfile</span>(<span class="hljs-params">userId: string</span>) </span>{
  <span class="hljs-keyword">const</span> queryClient = useQueryClient();

  <span class="hljs-keyword">return</span> useMutation({
    <span class="hljs-attr">mutationFn</span>: updateProfileRequest,
    <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-comment">// Invalidate the same key your UI query uses (example: ["user", userId])</span>
      queryClient.invalidateQueries({ <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">"user"</span>, userId] });
    },
  });
}
</code></pre>
<p>If your UI uses a different key (for example <code>["me"]</code> or <code>["user", userId, "profile"]</code>), you must invalidate that key instead, React Query won’t “figure it out” from the URL.</p>
<h3 id="heading-query-keys-react-query-caches-by-key-not-url">Query Keys: React Query Caches by Key, not URL</h3>
<p>React Query does not cache by endpoint URL. The query key is the identity of the cached data. If two different requests share the same key, React Query treats them as the same data and they can overwrite each other.</p>
<p>You should avoid keys like <code>["user"]</code> (too broad), and use keys like <code>["user", userId]</code> and <code>["users", { page, search, filter }]</code>.</p>
<p><strong>Two settings that control “when it will refetch”:</strong></p>
<ul>
<li><p><strong>staleTime:</strong> how long cached data is treated as fresh. While data is fresh, React Query is less likely to refetch automatically.</p>
</li>
<li><p><strong>gcTime (formerly cacheTime):</strong> how long unused query data stays in memory after it’s no longer used by any component, before it’s garbage collected.</p>
</li>
</ul>
<h2 id="heading-cache-2-nextjs-fetch-caching">Cache 2: Next.js fetch() Caching</h2>
<p>This is the one that surprises a lot of frontend devs. Next.js can cache results to speed things up. That means your server might return a previously saved copy of:</p>
<ul>
<li><p>The API data it fetched, or</p>
</li>
<li><p>The page it already built</p>
</li>
</ul>
<p>This is often the first time frontend developers encounter server-side caching behavior that affects UI correctness. So, even if your database has the new value, you can still see the old one, because Next.js didn’t fetch the API again, or didn’t rebuild the page this time.</p>
<p>This mainly applies to the App Router (Next.js calls these saved copies the Data Cache and Full Route Cache).</p>
<h3 id="heading-what-youll-notice-when-this-happens">What you’ll notice when this happens</h3>
<ul>
<li><p>You refresh the page and it still shows the old value.</p>
</li>
<li><p>Your API is correct (Postman/curl shows the new email), but the UI is stuck.</p>
</li>
<li><p>Sometimes it “fixes itself” after a short wait (because the saved copy refreshes on a timer).</p>
</li>
</ul>
<p>For example: “I updated my profile email, but prod still shows the old one”</p>
<p>The page (reads email on the server):</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// app/settings/page.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SettingsPage</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://api.example.com/users/42"</span>, {
  <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
})
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> res.json();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Settings<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Email: {user.email}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>You submit an “Update email” form, the API returns <strong>200 OK</strong>, the database is updated, but /settings still shows the previous email in production.</p>
<p>That usually means you’re seeing a saved copy somewhere on the server side.</p>
<h3 id="heading-how-to-debug-it">How to debug it</h3>
<h4 id="heading-step-1-reproduce-in-a-production-like-run">Step 1: Reproduce in a production-like run</h4>
<p>Caching can behave differently in development. Run:</p>
<pre><code class="lang-bash">next build &amp;&amp; next start
</code></pre>
<p>Then test again.</p>
<h4 id="heading-step-2-confirm-whether-the-request-is-reaching-your-nextjs-server-at-all">Step 2: Confirm whether the request is reaching your Next.js server at all</h4>
<p>Add a log inside the page:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Rendering /settings at"</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString());
</code></pre>
<p>Then reload settings twice.</p>
<ul>
<li><p>If you see a new timestamp every reload, the request is reaching your server and the page code is running.</p>
</li>
<li><p>If you don’t see logs in production, your request may not be reaching your server at all (often because a hosting/CDN layer is serving a saved copy before Next.js runs). You’ll confirm that in the CDN section later.</p>
</li>
</ul>
<h4 id="heading-step-3-force-nextjs-to-ask-your-api-every-time">Step 3: Force Next.js to ask your API every time</h4>
<p>Change the fetch to:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://api.example.com/me"</span>, {
  <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
  <span class="hljs-attr">cache</span>: <span class="hljs-string">"no-store"</span>,
});
</code></pre>
<p>This means: don’t save this response – always fetch it again.</p>
<p>If this fixes the stale email then the problem was a saved copy of the API response (Data Cache).</p>
<h4 id="heading-step-4-if-the-email-is-still-stale-force-nextjs-to-rebuild-the-page-every-request">Step 4: If the email is still stale, force Next.js to rebuild the page every request</h4>
<p>Add this to the page file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// app/settings/page.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> dynamic = <span class="hljs-string">"force-dynamic"</span>;
</code></pre>
<p>This means: don’t serve a saved copy of the page; rebuild it per request.</p>
<h3 id="heading-a-beginner-safe-setup-for-the-user-settings-pages-with-some-of-the-suggestions">A “beginner-safe” setup for the user settings pages with some of the suggestions:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// app/settings/page.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> dynamic = <span class="hljs-string">"force-dynamic"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SettingsPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://api.example.com/me"</span>, { <span class="hljs-attr">cache</span>: <span class="hljs-string">"no-store"</span> });
  <span class="hljs-keyword">const</span> me = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Email: {me.email}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
}
</code></pre>
<p>When you want caching for speed, but still need real time updates, these are some options you can take:</p>
<h4 id="heading-option-a-refresh-the-saved-copy-every-n-seconds">Option A: Refresh the saved copy every N seconds</h4>
<p>Good for public pages, not ideal for “my settings must update now.”</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> fetch(url, { <span class="hljs-attr">next</span>: { <span class="hljs-attr">revalidate</span>: <span class="hljs-number">60</span> } });
</code></pre>
<p>This means: “You can reuse a saved copy, but refresh it at most every 60 seconds.”</p>
<h4 id="heading-option-b-refresh-right-after-the-update-best-for-update-email-flows">Option B: Refresh right after the update (best for “update email” flows)</h4>
<p>If you update the email on the server (Server Action or API route), tell Next.js to throw away the saved copy for /settings page so the next visit is fresh:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// app/settings/actions.ts</span>
<span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">import</span> { revalidatePath } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/cache"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateEmail</span>(<span class="hljs-params">email: string</span>) </span>{
  <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://api.example.com/me/email"</span>, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"PUT"</span>,
    <span class="hljs-attr">headers</span>: { <span class="hljs-string">"content-type"</span>: <span class="hljs-string">"application/json"</span> },
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ email }),
  });

  <span class="hljs-comment">// Tell Next.js: next request to /settings should be rebuilt</span>
  revalidatePath(<span class="hljs-string">"/settings"</span>);
}
</code></pre>
<p><strong>Note</strong>: Next.js caching details can differ by version and by App Router vs Pages Router. Instead of trying to memorize defaults, debug by setting the behavior explicitly (no-store, revalidate, force-dynamic) and observe what changes.</p>
<h2 id="heading-cache-3-browser-http-cache-a-saved-copy-in-your-browser">Cache 3: Browser HTTP Cache (a Saved Copy in Your Browser)</h2>
<p>Sometimes the browser reuses a saved copy of an API response (from memory or disk), so it doesn’t fully fetch it again.</p>
<h3 id="heading-what-youll-notice">What you’ll notice</h3>
<p>You open DevTools, and the network shows (from memory cache) or (from disk cache).</p>
<h3 id="heading-fast-check">Fast check</h3>
<p>DevTools → Network</p>
<ul>
<li><p>Turn on Disable cache (only works while DevTools is open)</p>
</li>
<li><p>Reload and retry</p>
</li>
</ul>
<h3 id="heading-why-it-happens">Why it happens</h3>
<p>Usually your server allows caching via headers like Cache-Control or ETag (which can lead to 304 Not Modified).</p>
<h2 id="heading-cache-4-cdnhosting-cache">Cache 4: CDN/Hosting Cache</h2>
<p>This is often a production-only cache, which is why frontend bugs can appear “impossible” to reproduce locally. In production, a CDN/hosting layer can serve a saved copy of a response before your request reaches your server. That’s why “prod is stale, local is fine” happens.</p>
<h3 id="heading-what-youll-notice-1">What you’ll notice</h3>
<ul>
<li><p>Prod is stale, local is fine</p>
</li>
<li><p>Different users see different results (different regions/POPs)</p>
</li>
<li><p>Pages are very fast even right after data changed</p>
</li>
</ul>
<h3 id="heading-fast-check-1">Fast check</h3>
<p>Open DevTools → Network → click the request → Response Headers</p>
<ul>
<li><p>Age: if present and increasing, it’s strong evidence you’re getting a cached response from an intermediary cache</p>
</li>
<li><p>Provider headers can hint HIT/MISS (examples: x-vercel-cache, cf-cache-status)</p>
</li>
<li><p>Source (Age header, HTTP caching): <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc9111">https://www.rfc-editor.org/rfc/rfc9111</a></p>
</li>
</ul>
<h3 id="heading-quick-diagnostic-check">Quick diagnostic check</h3>
<p>Change the URL slightly by adding this to the end of the URL:</p>
<pre><code class="lang-javascript">?debug=<span class="hljs-number">1700000000000</span>
</code></pre>
<p>If the new URL shows fresh data, the edge was likely caching the original URL. This doesn’t fix it for everyone, you’d still need correct cache settings or a purge/invalidation on your CDN.</p>
<h2 id="heading-cache-5-service-worker-cache-only-if-your-site-is-a-pwa">Cache 5: Service Worker Cache (Only if Your Site is a PWA)</h2>
<p>If your site has a service worker, it can return a saved response before the network runs. This can make new deployments or new data seem “ignored.”</p>
<h3 id="heading-what-youll-notice-2">What you’ll notice</h3>
<ul>
<li><p>Works in Incognito but not normal mode</p>
</li>
<li><p>Hard refresh doesn’t help</p>
</li>
<li><p>DevTools “Disable cache” doesn’t fully explain it</p>
</li>
</ul>
<h3 id="heading-fast-check-chrome">Fast check (Chrome)</h3>
<p>Open DevTools → Application → Service Workers</p>
<ul>
<li><p>enable Bypass for network, or Unregister temporarily</p>
</li>
<li><p>reload and retest</p>
</li>
</ul>
<h2 id="heading-10-second-debug-guide">10-Second Debug Guide</h2>
<p>Stale data is rarely random: it usually means a cache layer is doing its job, just not in the way you expect. Modern applications stack multiple caches, so debugging is less about fixing code immediately and more about locating the layer responsible.</p>
<p>Think of this as a quick cheat sheet to figure out which cache layer might be serving stale data, so you can focus your debugging on the right layer.</p>
<ul>
<li><p>No request in Network? Go to <code>Cache 1 (React Query)</code>, then Local state, then <code>Cache 5 (Service worker)</code>.</p>
</li>
<li><p>Request exists, but response is old? Go to <code>Cache 3 (Browser)</code>, <code>Cache 4 (CDN)</code>, then <code>Cache 2 (Next.js)</code>.</p>
</li>
<li><p>Response is fresh, UI is old? Go back to <code>Cache 1 (invalidating / query keys)</code> and Local state.</p>
</li>
</ul>
<p>Once you know the likely layer, use the Fast check in that section to confirm it.</p>
<h2 id="heading-prevention-set-caching-intentionally">Prevention: Set Caching Intentionally</h2>
<p>Most stale-data bugs happen because caching settings were never chosen but the defaults were.</p>
<ul>
<li><p>User-specific pages (settings/admin/dashboard): default to fresh: Next.js: use cache: "no-store" on important fetches, and/or force dynamic routes when needed.</p>
</li>
<li><p>Public pages (marketing/blog/docs): saving + revalidate is usually fine: Decide a revalidate window that matches the business need (seconds/minutes/hours).</p>
</li>
<li><p>React Query: set staleTime based on how often the data actually changes, and make query keys match the inputs.</p>
</li>
<li><p>APIs: set Cache-Control / Vary intentionally so shared caches don’t mix user-specific responses.</p>
</li>
</ul>
<h2 id="heading-recap">Recap</h2>
<p>Caching itself isn’t the problem. Stale UI happens when a cache exists but you didn’t choose it intentionally or align it with the data’s freshness requirements.</p>
<p>If the UI won’t update, it’s usually because you’re seeing a saved copy from React Query, Next.js, the browser, a CDN, or a service worker. And sometimes it’s not a cache at all, it’s local React state</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Cache Golang API Responses for High Performance ]]>
                </title>
                <description>
                    <![CDATA[ Go makes it easy to build APIs that are fast out of the box. But as usage grows, speed at the language level is not enough. If every request keeps hitting the database, crunching the same data, or serializing the same JSON over and over, latency cree... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-cache-golang-api-responses/</link>
                <guid isPermaLink="false">68ef76f4e9381bb61f442e34</guid>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cache ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temitope Oyedele ]]>
                </dc:creator>
                <pubDate>Wed, 15 Oct 2025 10:27:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760523799795/3b48a898-77fc-4983-90b5-6e21e8019f1e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Go makes it easy to build APIs that are fast out of the box. But as usage grows, speed at the language level is not enough. If every request keeps hitting the database, crunching the same data, or serializing the same JSON over and over, latency creeps up and throughput suffers. Caching is the tool that keeps performance high by storing work that has already been done so that future requests can reuse it instantly. Let’s look at four practical ways to cache APIs in Go, each explained with an analogy and backed by simple code you can adapt.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-response-caching-with-local-and-redis-storage">Response Caching with Local and Redis Storage</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-database-query-result-caching">Database Query Result Caching</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-http-caching-with-etag-and-cache-control">HTTP Caching with ETag and Cache-Control</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stale-while-revalidate-with-background-refresh">Stale-While-Revalidate with Background Refresh</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-response-caching-with-local-and-redis-storage">Response Caching with Local and Redis Storage</h2>
<p>When the process of generating an API response becomes expensive, the fastest solution is to store the entire response. Think of a coffee shop during the morning rush. If every customer orders the same latte, the barista could grind beans and steam milk for each order, but the line would move slowly. A smarter move is to brew a pot once and pour from it repeatedly. To handle both speed and scale, the shop keeps a small pot at the counter for instant pours and a larger urn in the back for refills. In software terms, the counter pot is a local in-memory cache such as <a target="_blank" href="https://pkg.go.dev/github.com/dgraph-io/ristretto">Ristretto</a> or <a target="_blank" href="https://pkg.go.dev/github.com/allegro/bigcache">BigCache</a>, and the urn is <a target="_blank" href="https://redis.io/">Redis,</a> which allows multiple API servers to share the same cached responses.</p>
<p>In Go, this two-tier setup usually follows a cache-aside pattern: look in local memory first, fall back to Redis if needed, and only compute the result when both layers miss. Once computed, the value is saved in Redis for everyone and in memory for immediate reuse on the next call.</p>
<pre><code class="lang-go">val, ok := local.Get(key)
<span class="hljs-keyword">if</span> !ok {
    val, err = rdb.Get(ctx, key).Result()
    <span class="hljs-keyword">if</span> err == redis.Nil {
        val = computeResponse() <span class="hljs-comment">// expensive DB or logic</span>
        _ = rdb.Set(ctx, key, val, <span class="hljs-number">60</span>*time.Second).Err()
    }
    local.Set(key, val, <span class="hljs-number">1</span>)
}
w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
w.Write([]<span class="hljs-keyword">byte</span>(val))
</code></pre>
<p>In the code above, the first attempt is to retrieve the response from the local cache, which returns instantly if the key or data exists. If not found, it queries Redis as the second layer. If Redis also returns nothing, the expensive computation runs and its result is stored in Redis with a sixty seconds expiration so other services can access it, then placed in the local cache for immediate reuse. After which, the response is written back to the client as JSON.</p>
<p>This gives you the best of both worlds: lightning-fast responses for repeat calls and a consistent cache across all your API servers.</p>
<h2 id="heading-database-query-result-caching">Database Query Result Caching</h2>
<p>Sometimes the API itself is simple but the real cost hides in the database. Imagine a newsroom waiting for election results. If every editor keeps calling the counting office for the same numbers, the phone lines may jam. Instead, one reporter calls once, writes the result on a board, and every editor copies from there. The board is the cache, and it saves both time and pressure on the office.</p>
<p>In Go, you can apply the same principle by caching query results. Rather than hitting the database for each identical request, you store the result in Redis with a key that represents the query intent. When the next request comes in, you pull from Redis, skip the database, and respond faster.</p>
<pre><code class="lang-go">key := fmt.Sprintf(<span class="hljs-string">"q:UserByID:%d"</span>, id)
<span class="hljs-keyword">if</span> b, err := rdb.Get(ctx, key).Bytes(); err == <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">var</span> u User
    _ = json.Unmarshal(b, &amp;u)
    <span class="hljs-keyword">return</span> u
}

u, _ := repo.GetUser(ctx, id) <span class="hljs-comment">// real DB call</span>
bb, _ := json.Marshal(u)
_ = rdb.Set(ctx, key, bb, <span class="hljs-number">2</span>*time.Minute).Err()
<span class="hljs-keyword">return</span> u
</code></pre>
<p>Here, we construct a cache key that uniquely identifies the query using the user ID, then attempts to fetch the serialized result from Redis. If the key exists, it deserializes the bytes back into a <code>User</code> struct and returns immediately without touching the database. On a cache miss, it executes the actual database query through the repository, serializes the <code>User</code> object to JSON, stores it in Redis with a two-minute expiration, and returns the result.</p>
<p>This pattern dramatically reduces database load and response time for read-heavy APIs, but you must remember to clear or refresh entries when data changes, or set short time-to-live values to keep results reasonably fresh.</p>
<h2 id="heading-http-caching-with-etag-and-cache-control">HTTP Caching with ETag and Cache-Control</h2>
<p>Not all caching has to happen inside the server. The HTTP standard already provides tools that let clients or CDNs reuse responses. By setting headers like <code>ETag</code> and <code>Cache-Control</code>, you can tell the client whether the response has changed. If nothing is new, the client keeps its own copy and the server only sends a lightweight 304 response.</p>
<p>It is similar to a manager posting notices on an office board. Each sheet carries a small stamp. Employees compare the stamp against the one they already have. If it matches, they know their copy is still valid and skip taking a new one. Only when the stamp changes do they replace it.</p>
<p>In Go this is straightforward. Compute an ETag from the response body, compare it with what the client sends, and decide whether to return the full payload or just the 304.</p>
<pre><code class="lang-go">etag := computeETag(responseBytes)
<span class="hljs-keyword">if</span> match := r.Header.Get(<span class="hljs-string">"If-None-Match"</span>); match == etag {
    w.WriteHeader(http.StatusNotModified)
    <span class="hljs-keyword">return</span>
}

w.Header().Set(<span class="hljs-string">"ETag"</span>, etag)
w.Header().Set(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"public, max-age=60"</span>)
w.Write(responseBytes)
</code></pre>
<p>The code above generates an ETag, which is a fingerprint or hash of the response content, then checks if the client sent an <code>If-None-Match</code> header with a matching ETag from a previous request. If the ETags match, the content hasn't changed, so the server responds with a 304 Not Modified status and sends no body, saving bandwidth. When the ETags don't match or the client has no cached version, the server attaches the new ETag and a <code>Cache-Control</code> header that allows public caching for sixty seconds, then sends the full response.</p>
<p>This approach reduces bandwidth, lowers CPU usage, and pairs well with CDNs that can cache and serve responses directly.</p>
<h2 id="heading-stale-while-revalidate-with-background-refresh">Stale-While-Revalidate with Background Refresh</h2>
<p>There are cases where serving slightly old data is acceptable if it keeps the API fast. Stock dashboards, analytics summaries, or feed endpoints often fit this model. Instead of making users wait for fresh data on every request, you can serve the cached value immediately and refresh it quietly in the background. This technique is called Stale-While-Revalidate.</p>
<p>Picture a stock ticker screen in a lobby. The numbers may be a few seconds behind, but they are still useful to anyone glancing at the board. Meanwhile, a background process fetches the latest figures and updates the ticker. The reader never stares at a blank screen and the system stays responsive even during spikes.</p>
<p>In Go, this can be built by storing not just the cached data but also timestamps that define when the data is fresh, when it can still be served as stale, and when it must be recomputed. The <code>singleflight</code> package helps ensure that only one goroutine does the refresh work, preventing a dogpile of updates.</p>
<pre><code class="lang-go">entry := getEntry(key) <span class="hljs-comment">// {data, freshUntil, staleUntil}</span>
<span class="hljs-keyword">switch</span> {
<span class="hljs-keyword">case</span> time.Now().Before(entry.freshUntil):
    <span class="hljs-keyword">return</span> entry.data
<span class="hljs-keyword">case</span> time.Now().Before(entry.staleUntil):
    <span class="hljs-keyword">go</span> refreshSingleflight(key) <span class="hljs-comment">// background refresh</span>
    <span class="hljs-keyword">return</span> entry.data
<span class="hljs-keyword">default</span>:
    <span class="hljs-keyword">return</span> refreshSingleflight(key) <span class="hljs-comment">// must refresh now</span>
}
</code></pre>
<p>Here, the code retrieves a cache entry containing the data along with two timestamps marking the freshness and staleness boundaries. If the current time falls before the fresh threshold, the data is considered fully fresh and returned immediately. If time has passed the fresh threshold but remains within the stale window, the code returns the slightly outdated data instantly while launching a background goroutine to refresh it asynchronously, ensuring the next request gets updated information. Once time exceeds even the stale boundary, the data is too old to serve, so the code blocks and performs a synchronous refresh before returning.</p>
<p>This keeps latency low while still ensuring the cache updates regularly, a balance between freshness and performance.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Caching is not a single tactic but a set of strategies that fit different needs. Full response caching eliminates repeat work at the top level. Query result caching protects the database from repeated load. HTTP caching leverages the protocol to cut down data transfer. Stale-While-Revalidate strikes a compromise that favors speed without leaving data stale for too long.</p>
<p>In practice, these approaches are often layered. A Go API might use local memory and Redis for responses, apply query-level caching for hot tables, and set ETags so clients avoid unnecessary downloads. With the right mix, you can cut latency by orders of magnitude, handle far more traffic, and save both compute and database resources.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Caching a Next.js API using Redis and Sevalla ]]>
                </title>
                <description>
                    <![CDATA[ When you hear about Next.js, your first thought may be static websites or React-driven frontends. But that’s just part of the story. Next.js can also power full-featured backend APIs that you can host and scale just like any other backend service. In... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/caching-a-nextjs-api-using-redis-and-sevalla/</link>
                <guid isPermaLink="false">68af2baaa845284174a27982</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Wed, 27 Aug 2025 16:00:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756310410998/ee5f34fd-0efe-4efc-9e91-2baa826edff9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you hear about Next.js, your first thought may be static websites or React-driven frontends. But that’s just part of the story. Next.js can also power full-featured backend APIs that you can host and scale just like any other backend service.</p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-deploy-a-nextjs-api-with-postgresql-and-sevalla/">In an earlier article</a>, I walked through building a Next.js API and deploying it with Sevalla. The example stored data in a PostgreSQL database and handled requests directly. That worked fine, but as traffic grows, APIs that hit the database on every request can slow down.</p>
<p>This is where caching comes in. By adding Redis as a cache layer, we can make our Next.js API much faster and more efficient. In this article, we’ll see how to add Redis caching to our API, deploy it with <a target="_blank" href="https://sevalla.com/">Sevalla</a>, and show measurable improvements.</p>
<p>In the last article, I explained the API in detail. So you can <a target="_blank" href="https://github.com/manishmshiva/nextjs-api-pgsql">use this repository</a> to start with as the base for this project.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-caching-matters">Why Caching Matters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-redis">What is Redis</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-project">Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-provisioning-redis">Provisioning Redis</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-updating-cache-on-reads">Updating Cache on Reads</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-updating-cache-on-writes">Updating Cache on Writes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-deploying-to-sevalla">Deploying to Sevalla</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-redis-works-well-with-nextjs-apis">Why Redis Works Well with Next.js APIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-caching-matters">Why Caching Matters</h2>
<p>Every time your API hits the database, it consumes time and resources. Databases are great at storing and querying structured data, but they aren’t optimized for speed at scale when you need to serve thousands of read requests per second.</p>
<p>Caching solves this by keeping frequently accessed data in memory. Instead of asking the database every time, the API can return data directly from cache if it’s available. Redis is perfect for this because it’s an in-memory key-value store designed for performance.</p>
<p>For example, if you fetch the list of users from the database on every request, it might take 200ms to run the query and return results. With Redis caching, the first request stores the result in memory, and subsequent requests can return the same data in less than 10ms. That’s an order-of-magnitude improvement.</p>
<h2 id="heading-what-is-redis">What is Redis?</h2>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-in-memory-caching-works-in-redis/">Redis</a> is an in-memory data store that works like a super-fast database. Instead of writing and reading from disk, it keeps data in memory, which makes it incredibly fast. That’s why it’s often used as a cache, where speed is more important than long-term storage.</p>
<p>It’s designed to handle high-throughput workloads with very low latency, which means it can respond in microseconds. This makes it a perfect fit for use cases like caching API responses, storing session data, or even powering real-time applications like chat systems and leaderboards.</p>
<p>Unlike a traditional database, Redis focuses on simplicity and speed. It stores data as key-value pairs, so you can quickly fetch or update values without writing complex queries. And because it supports advanced data types like lists, sets, and hashes, it’s much more flexible than a plain key-value store.</p>
<p>When combined with an API like the one we built in Next.js, Redis helps you reduce load on the main database and deliver blazing-fast responses to clients.</p>
<h2 id="heading-setting-up-the-project">Setting Up the Project</h2>
<p>Let’s clone the repository:</p>
<pre><code class="lang-typescript">git clone git<span class="hljs-meta">@github</span>.com:manishmshiva/nextjs-api-pgsql.git next-api
</code></pre>
<p>Now let’s go into the directory and do an npm install to install the packages.</p>
<pre><code class="lang-typescript">cd next-api
npm i
</code></pre>
<p>Create a .env file and add the database URL from Sevalla into an environment variable.</p>
<pre><code class="lang-typescript">cat .env
</code></pre>
<p>The .env file should look like this:</p>
<pre><code class="lang-typescript">PGSQL_URL=postgres:<span class="hljs-comment">//&lt;username&gt;:&lt;password&gt;-@asia-east1-001.proxy.kinsta.app:30503/&lt;db_name&gt;</span>
</code></pre>
<p>Now let’s make sure the application works as expected by starting the application and making a couple of API requests.</p>
<p>Starting the app:</p>
<pre><code class="lang-typescript">npm run dev
</code></pre>
<p>Let’s make sure the database is connected. Go to <code>localhost:3000</code> on your browser. It should return the following JSON:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755607650708/543df6fe-3bea-4eb2-b962-13df35b6fb2c.png" alt="GET /" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Let’s create a new user. To create a new entry in the DB using <a target="_blank" href="https://www.postman.com/">Postman</a>, send a POST request with the following JSON:</p>
<pre><code class="lang-typescript">{<span class="hljs-string">"id"</span>:<span class="hljs-string">"d9553bb7-2c72-4d92-876b-9c3b40a8c62c"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"Larry"</span>,<span class="hljs-string">"email"</span>:<span class="hljs-string">"larry@example.com"</span>,<span class="hljs-string">"age"</span>:<span class="hljs-string">"25"</span>}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755607596858/7c8c71b5-7868-47a1-ae8d-172474f6d75b.png" alt="Postman test" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Let’s ensure the record is created by going to <code>localhost:3000/users</code> in the browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755607717319/d6743d2a-8373-4d81-afee-1f034e1954e1.png" alt="Postman response for /users" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Great. Now let’s cache these APIs using Redis.</p>
<h2 id="heading-provisioning-redis">Provisioning Redis</h2>
<p>Let’s go to <a target="_blank" href="https://app.sevalla.com/login">Sevalla’s</a> dashboard and click on “Databases”. Choose “Redis” from the list, and leave the rest of the options as defaults.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755415913475/0ba7badb-2c67-474a-a5b1-6d72b5bdc5f3.png" alt="Create database" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Once the database is created, switch on the “external connection” option and copy the publicly accessible URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755611728877/6e139e09-8484-4a50-b007-32ecdb266afb.png" alt="Update network settings" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This is how it should look in the .env file:</p>
<pre><code class="lang-typescript">REDIS_URL=redis:<span class="hljs-comment">//default:&lt;password&gt;@&lt;host&gt;:&lt;port&gt;</span>
</code></pre>
<p>Now install a Redis client for Node.js:</p>
<pre><code class="lang-bash">npm install ioredis
</code></pre>
<p>We can now connect to Redis and use it as a cache layer for our users API. Let’s see how to implement caching.</p>
<h2 id="heading-updating-cache-on-reads">Updating Cache on Reads</h2>
<p>Here’s the updated <code>users/route.ts</code> that uses Redis:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"pg"</span>;
<span class="hljs-keyword">import</span> Redis <span class="hljs-keyword">from</span> <span class="hljs-string">"ioredis"</span>;

<span class="hljs-keyword">const</span> redis = <span class="hljs-keyword">new</span> Redis(process.env.REDIS_URL!);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">readUsers</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
    connectionString: process.env.PGSQL_URL,
  });
  <span class="hljs-keyword">await</span> client.connect();

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">"SELECT id, name, email, age FROM users"</span>);
    <span class="hljs-keyword">return</span> result.rows;
  } <span class="hljs-keyword">finally</span> {
    <span class="hljs-keyword">await</span> client.end();
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Check cache first</span>
    <span class="hljs-keyword">const</span> cached = <span class="hljs-keyword">await</span> redis.get(<span class="hljs-string">"users"</span>);
    <span class="hljs-keyword">if</span> (cached) {
      <span class="hljs-keyword">return</span> NextResponse.json(<span class="hljs-built_in">JSON</span>.parse(cached));
    }

    <span class="hljs-comment">// Fallback to database if not cached</span>
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> readUsers();

    <span class="hljs-comment">// Store result in cache with 60s TTL</span>
    <span class="hljs-keyword">await</span> redis.set(<span class="hljs-string">"users"</span>, <span class="hljs-built_in">JSON</span>.stringify(users), <span class="hljs-string">"EX"</span>, <span class="hljs-number">60</span>);

    <span class="hljs-keyword">return</span> NextResponse.json(users);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> NextResponse.json({ error: <span class="hljs-string">"Failed to fetch users"</span> }, { status: <span class="hljs-number">500</span> });
  }
}
</code></pre>
<p>Now, when you hit <code>/users</code>:</p>
<ol>
<li><p>The API first checks Redis.</p>
</li>
<li><p>If the data exists, it returns it instantly.</p>
</li>
<li><p>If not, it queries PostgreSQL, saves the result in Redis, and then returns it.</p>
</li>
</ol>
<p>This makes repeated requests extremely fast. You can adjust the cache expiry (<code>EX 60</code>) depending on how fresh your data needs to be.</p>
<p>Without Redis caching, fetching <code>/users</code> ten times means ten database queries. Each might take around 150–200ms depending on database size and network latency.</p>
<p>With Redis, the first request still takes ~200ms since it populates the cache. But every request after that is nearly instant, often under 10ms. That’s a <strong>20x improvement</strong>.</p>
<p>This speedup matters when your API faces hundreds or thousands of requests per second. Caching not only reduces latency but also lightens the load on your database.</p>
<h2 id="heading-updating-cache-on-writes">Updating Cache on Writes</h2>
<p>Right now, only GET requests use the cache. But what if we add new users? The cache would still return the old data.</p>
<p>The solution is to update or clear the cache whenever a write happens. Let’s update the <code>POST</code> handler:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: Request</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> req.json();
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
      connectionString: process.env.PGSQL_URL,
    });
    <span class="hljs-keyword">await</span> client.connect();

    <span class="hljs-keyword">const</span> query = <span class="hljs-string">`
      INSERT INTO users (id, name, email, age)
      VALUES ($1, $2, $3, $4)
      RETURNING *;
    `</span>;

    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.query(query, [
      body.id,
      body.name,
      body.email,
      body.age,
    ]);

    <span class="hljs-keyword">await</span> client.end();

    <span class="hljs-comment">// Invalidate cache so next GET fetches fresh data</span>
    <span class="hljs-keyword">await</span> redis.del(<span class="hljs-string">"users"</span>);

    <span class="hljs-keyword">return</span> NextResponse.json(result.rows[<span class="hljs-number">0</span>]);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> NextResponse.json({ error: <span class="hljs-string">"Failed to add user"</span> }, { status: <span class="hljs-number">500</span> });
  }
}
</code></pre>
<p>Now whenever a new user is created, the cache for <code>users</code> is cleared. The next GET request will fetch from the database, refresh the cache, and then continue serving cached data.</p>
<h2 id="heading-deploying-to-sevalla"><strong>Deploying to Sevalla</strong></h2>
<p>Push your code to GitHub or <a target="_blank" href="https://github.com/manishmshiva/nextjs-api-redis">fork my repository</a>. Now lets go to Sevalla and create a new app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754545093624/9747a06d-0dcf-482a-89b9-732b9937b1dc.png" alt="Sevalla create app" width="600" height="400" loading="lazy"></p>
<p>Choose your repository from the dropdown and check “Automatic deployment on commit”. This will ensure that the deployment is automatic every time you push code. Choose “Hobby” under the resources section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755683871627/cc8bd555-caaa-43f2-a9a3-f3f0d1481108.png" alt="create new app" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Click “Create” and not “Create and deploy”. We haven’t added our PostgreSQL URL and Redis URL as environment variables, so the app will crash if you try to deploy it.</p>
<p>Go to the “Environment variables” section and add the key “PGSQL_URL” and the URL in the value field. Do the same for the “REDIS_URL” key and add the Redis URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755683943610/97932885-29aa-4cef-b999-689f0871809e.png" alt="Adding environment variables" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now go back to the “Overview” section and click “Deploy now”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755684196447/a1da5802-aa2c-47f6-8837-14f7e40198fd.png" alt="App Deployment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Once deployment is complete, click “Visit app” to get the live URL of your API. You can replace <a target="_blank" href="http://localhost:3000">localhost:3000</a> with the new URL in Postman and test your API.</p>
<h2 id="heading-why-redis-works-well-with-nextjs-apis">Why Redis Works Well with Next.js APIs</h2>
<p>Redis is lightweight, blazing fast, and perfect for caching API responses. In the context of Next.js, it fits naturally because:</p>
<ul>
<li><p>The API routes run server-side where Redis can be queried directly.</p>
</li>
<li><p>Caching logic is simple to add around database calls.</p>
</li>
<li><p>Redis can be used for more than caching – things like rate limiting, session storage, and pub/sub are also common patterns.</p>
</li>
</ul>
<p>By combining Next.js, PostgreSQL, and Redis on Sevalla, you get a stack that is fast, scalable, and easy to deploy.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Caching isn’t just an optimization – it’s a necessity for real-world APIs. Next.js helps you build robust backend APIs that can be deployed easily. By adding Redis to the mix, those APIs can handle scale without breaking a sweat.</p>
<p>Sevalla ties it all together by providing managed PostgreSQL, Redis, and app hosting in one place. With a few environment variables and a GitHub repo, you can go from local dev to a production-ready, cached API in minutes.</p>
<p><em>Hope you enjoyed this article. Signup for my free AI newsletter</em> <a target="_blank" href="https://www.turingtalks.ai/"><strong><em>TuringTalks.ai</em></strong></a> <em>for more hands-on tutorials on AI. You can also find me on</em> <a target="_blank" href="https://www.linkedin.com/in/manishmshiva"><strong><em>Linkedin</em></strong></a><strong><em>.</em></strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How In-Memory Caching Works in Redis ]]>
                </title>
                <description>
                    <![CDATA[ When you’re building a web app or API that needs to respond quickly, caching is often the secret sauce. Without it, your server can waste time fetching the same data over and over again – from a database, a third-party API, or a slow storage system. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-in-memory-caching-works-in-redis/</link>
                <guid isPermaLink="false">6877d117ba632c15e613aac8</guid>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Wed, 16 Jul 2025 16:19:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752680755362/97cde2e5-3bb3-4b5d-b073-dcbf03c7f871.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re building a web app or API that needs to respond quickly, caching is often the secret sauce.</p>
<p>Without it, your server can waste time fetching the same data over and over again – from a database, a third-party API, or a slow storage system.</p>
<p>But when you store that data in memory, the same information can be served up in milliseconds. That’s where Redis comes in.</p>
<p>Redis is a fast, flexible tool that stores your data in RAM and lets you retrieve it instantly. Whether you’re building a dashboard, automating social media posts, or managing user sessions, Redis can make your system faster, more efficient, and easier to scale.</p>
<p>In this article, you’ll learn how in-memory caching works and why Redis is a go-to choice for many developers.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-in-memory-caching">What Is In-Memory Caching?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-redis">What Is Redis?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-redis">How to Work with Redis</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-redis-installation">Redis Installation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-redis-data-types">Redis Data Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-redis-with-python">Redis with Python</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-real-life-use-cases">Real-Life Use Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-in-memory-caching"><strong>What Is In-Memory Caching?</strong></h2>
<p>In-memory caching is a way of storing data in the system’s RAM instead of fetching it from a database or external source every time it’s needed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752582672642/78e262d7-76a3-4bf3-886c-32a190d190b7.webp" alt="Diagram showing how caching works" class="image--center mx-auto" width="1100" height="776" loading="lazy"></p>
<p>Since RAM is incredibly fast compared to disk storage, you can access cached data almost instantly. This approach is perfect for information that doesn’t change very often, like API responses, user profiles, or rendered HTML pages.</p>
<p>Rather than repeatedly running the same queries or API calls, your app checks the cache first. If the data is there, it’s used right away. If it’s not, you fetch it from the source, save it to the cache, and then return it.</p>
<p>This technique reduces load on your backend, improves response time, and can dramatically improve your app’s performance under heavy traffic.</p>
<h2 id="heading-what-is-redis"><strong>What Is Redis?</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752582701613/951f7322-0c49-4437-b97b-6502bd93483a.webp" alt="Redis" class="image--center mx-auto" width="300" height="168" loading="lazy"></p>
<p><a target="_blank" href="https://redis.io/">Redis</a> is an open-source, in-memory data store that developers use to cache and manage data in real time.</p>
<p>Unlike traditional databases, Redis stores everything in memory, which makes data retrieval incredibly fast. But Redis isn’t just a simple key-value store. It offers a wide range of data types, from strings and lists to sets, hashes, and sorted sets.</p>
<p>Redis is also capable of handling more advanced tasks like pub/sub messaging, streams, and geospatial queries. Despite its power, Redis is lightweight and easy to get started with.</p>
<p>You can run it on your local machine, deploy it on a server, or even use managed Redis services offered by cloud providers. It’s trusted by major companies and used in all kinds of applications, from caching and session storage to real-time analytics and job queues.</p>
<h2 id="heading-how-to-work-with-redis"><strong>How to Work with Redis</strong></h2>
<h3 id="heading-redis-installation">Redis Installation</h3>
<p>Getting Redis up and running is surprisingly simple. You can find the installation instructions based on your operating system <a target="_blank" href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/">in the documentation</a>.</p>
<p>To make sure Redis is working, run:</p>
<pre><code class="lang-plaintext">redis-cli ping
# Should respond with "PONG"
</code></pre>
<h3 id="heading-redis-data-types">Redis Data Types</h3>
<p>Redis gives you several built-in types that let you store and manage data in flexible ways.</p>
<p><strong>Strings</strong>: Simple key ↔ value pairs.</p>
<pre><code class="lang-plaintext">SET username "Emily"
GET username
</code></pre>
<p><strong>Lists</strong>: Ordered collections which are great for queues and timelines.</p>
<pre><code class="lang-plaintext">LPUSH tasks "task1"
RPUSH tasks "task2"
LRANGE tasks 0 -1
</code></pre>
<p><strong>Hashes</strong>: Like JSON objects, great for user profiles.</p>
<pre><code class="lang-plaintext">HSET user:1 name "Alice"
HSET user:1 email "alice@example.com"
HGETALL user:1
</code></pre>
<p><strong>Sets</strong>: Unordered collections, ideal for tags or unique items.</p>
<pre><code class="lang-plaintext">SADD tags "python"
SADD tags "redis"
SMEMBERS tags
</code></pre>
<p><strong>Sorted Sets</strong>: Sets with scores – useful for leaderboards.</p>
<pre><code class="lang-plaintext">ZADD leaderboard 100 "Bob"
ZADD leaderboard 200 "Carol"
ZRANGE leaderboard 0 -1 WITHSCORES
</code></pre>
<p>Redis also supports Bitmaps, hyperloglogs, streams, geospatial indexes, and keeps expanding its support for <a target="_blank" href="https://redis.io/technology/data-structures/">data structures</a>.</p>
<h3 id="heading-redis-with-python">Redis with Python</h3>
<p>If you’re working in Python, using Redis is just as easy. After installing the <code>redis</code> Python library using <code>pip install redis</code>, you can connect to your Redis server and start setting and getting keys right away.</p>
<p>Here is some simple <a target="_blank" href="https://www.freecodecamp.org/news/learn-python-free-python-courses-for-beginners/">Python code</a> to work with Redis:</p>
<pre><code class="lang-plaintext">import redis

# Connect to the local Redis server on default port 6379 and use database 0
r = redis.Redis(host='localhost', port=6379, db=0)

# --- Basic String Example ---

# Set a key called 'welcome' with a string value
r.set('welcome', 'Hello, Redis!')

# Get the value of the key 'welcome'
# Output will be a byte string: b'Hello, Redis!'
print(r.get('welcome'))


# --- Hash Example (like a Python dict) ---

# Create a Redis hash under the key 'user:1'
# This hash stores fields 'name' and 'email' for a user
r.hset('user:1', mapping={
    'name': 'Alice',
    'email': 'alice@example.com'
})

# Get all fields and values in the hash as a dictionary of byte strings
# Output: {b'name': b'Alice', b'email': b'alice@example.com'}
print(r.hgetall('user:1'))


# --- List Example (acts like a queue or stack) ---

# Push 'Task A' to the left of the list 'tasks'
r.lpush('tasks', 'Task A')

# Push 'Task B' to the left of the list 'tasks' (it becomes the first item)
r.lpush('tasks', 'Task B')

# Retrieve all elements from the list 'tasks' (from index 0 to -1, meaning the full list)
# Output: [b'Task B', b'Task A']
print(r.lrange('tasks', 0, -1))
</code></pre>
<p>You might store a user's session data, queue background tasks, or even cache rendered HTML pages. Redis commands are fast and atomic, which means you don’t have to worry about data collisions or inconsistency in high-traffic environments.</p>
<p>One of the most useful features in Redis is key expiration. You can tell Redis to automatically delete a key after a certain period, which is especially handy for session data or temporary caches.</p>
<p>You can set a time-to-live (TTL) on keys, so Redis removes them automatically</p>
<pre><code class="lang-plaintext">SET session:1234 "some data" EX 3600  # Expires in 1 hour
</code></pre>
<p>Redis also supports persistence, so even though it’s an in-memory store, your data can survive a reboot.</p>
<p>Redis isn’t limited to small apps. It scales easily through replication, clustering, and <a target="_blank" href="https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/">Sentinel</a>.</p>
<p>Replication allows you to create read-only copies of your data, which helps distribute the load. Clustering breaks your data into chunks and spreads them across multiple servers. And Sentinel handles automatic failover to keep your system running even if one server goes down.</p>
<h2 id="heading-real-life-use-cases"><strong>Real-Life Use Cases</strong></h2>
<p>One of the most common uses for Redis is caching API responses.</p>
<p>Let’s say you have an app that displays weather data. Rather than calling the <a target="_blank" href="https://openweathermap.org/api">weather API</a> every time a user loads the page, you can cache the response for each city in Redis for 5 or 10 minutes. That way, you only fetch new data occasionally, and your app becomes much faster and cheaper to run.</p>
<p>Another powerful use case is <a target="_blank" href="https://gtcsys.com/faq/what-are-the-best-practices-for-caching-and-session-management-in-web-application-development-2/">session management</a>. In web applications, every logged-in user has a session that tracks who they are and what they’re doing. Redis is a great place to store this session data because it’s fast and temporary.</p>
<p>You can store the session ID as a key, with the user’s information in a hash. Add an expiration time, and you’ve got automatic session timeout built in. Since Redis is so fast and supports high-concurrency access, it’s a great fit for applications with thousands of users logging in at the same time.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In-memory caching is one of the simplest and most effective ways to speed up your app, and Redis makes it incredibly easy to implement. It’s not just a cache, it’s a toolkit for building fast, scalable, real-time systems. You can start small by caching a few pages or API responses, and as your needs grow, Redis grows with you.</p>
<p>If you’re just getting started, try running Redis locally and experimenting with different data types. Store some strings, build a simple task queue with lists, or track user scores with a sorted set. The more you explore, the more you’ll see how Redis can help your application run faster, smarter, and more efficiently.</p>
<p>Enjoyed this article? <a target="_blank" href="https://www.linkedin.com/in/manishmshiva">Connect with me on Linkedin</a>. See you soon with another topic.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Scalable URL Shortener with Distributed Caching Using Redis ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we'll build a scalable URL shortening service using Node.js and Redis. This service will leverage distributed caching to handle high traffic efficiently, reduce latency, and scale seamlessly. We'll explore key concepts such as consi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-scalable-url-shortener-with-distributed-caching-using-redis/</link>
                <guid isPermaLink="false">673cab723502f2a00d61ece1</guid>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ consistent hashing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cache Invalidation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sharding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Url Shortener ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 15:14:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/b9-odQi5oDo/upload/934c8b697ce5ce612a217d47ddf5430d.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we'll build a scalable URL shortening service using Node.js and Redis. This service will leverage distributed caching to handle high traffic efficiently, reduce latency, and scale seamlessly. We'll explore key concepts such as consistent hashing, cache invalidation strategies, and sharding to ensure the system remains fast and reliable.</p>
<p>By the end of this guide, you'll have a fully functional URL shortener service that uses distributed caching to optimize performance. We'll also create an interactive demo where users can input URLs and see real-time metrics like cache hits and misses.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to build a URL shortener service using <strong>Node.js</strong> and <strong>Redis</strong>.</p>
</li>
<li><p>How to implement <strong>distributed caching</strong> to optimize performance.</p>
</li>
<li><p>Understanding <strong>consistent hashing</strong> and <strong>cache invalidation strategies</strong>.</p>
</li>
<li><p>Using <strong>Docker</strong> to simulate multiple Redis instances for sharding and scaling.</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before starting, make sure you have the following installed:</p>
<ul>
<li><p>Node.js (v14 or higher)</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Docker</p>
</li>
<li><p>Basic knowledge of JavaScript, Node.js, and Redis.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-redis-instances">Step 2: Setting Up Redis Instances</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-implementing-the-url-shortener-service">Step 3: Implementing the URL Shortener Service</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implementing-cache-invalidation">Step 4: Implementing Cache Invalidation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-monitoring-cache-metrics">Step 5: Monitoring Cache Metrics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-testing-the-application">Step 6: Testing the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</a></p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>We'll build a URL shortener service where:</p>
<ol>
<li><p>Users can shorten long URLs and retrieve the original URLs.</p>
</li>
<li><p>The service uses <strong>Redis caching</strong> to store mappings between shortened URLs and original URLs.</p>
</li>
<li><p>The cache is distributed across multiple Redis instances to handle high traffic.</p>
</li>
<li><p>The system will demonstrate <strong>cache hits</strong> and <strong>misses</strong> in real-time.</p>
</li>
</ol>
<h3 id="heading-system-architecture">System Architecture</h3>
<p>To ensure scalability and performance, we'll divide our service into the following components:</p>
<ol>
<li><p><strong>API Server</strong>: Handles requests for shortening and retrieving URLs.</p>
</li>
<li><p><strong>Redis Caching Layer</strong>: Uses multiple Redis instances for distributed caching.</p>
</li>
<li><p><strong>Docker</strong>: Simulates a distributed environment with multiple Redis containers.</p>
</li>
</ol>
<h2 id="heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Let's set up our project by initializing a Node.js application:</p>
<pre><code class="lang-javascript">mkdir scalable-url-shortener
cd scalable-url-shortener
npm init -y
</code></pre>
<p>Now, install the necessary dependencies:</p>
<pre><code class="lang-javascript">npm install express redis shortid dotenv
</code></pre>
<ul>
<li><p><code>express</code>: A lightweight web server framework.</p>
</li>
<li><p><code>redis</code>: To handle caching.</p>
</li>
<li><p><code>shortid</code>: For generating short, unique IDs.</p>
</li>
<li><p><code>dotenv</code>: For managing environment variables.</p>
</li>
</ul>
<p>Create a <strong>.env</strong> file in the root of your project:</p>
<pre><code class="lang-javascript">PORT=<span class="hljs-number">3000</span>
REDIS_HOST_1=localhost
REDIS_PORT_1=<span class="hljs-number">6379</span>
REDIS_HOST_2=localhost
REDIS_PORT_2=<span class="hljs-number">6380</span>
REDIS_HOST_3=localhost
REDIS_PORT_3=<span class="hljs-number">6381</span>
</code></pre>
<p>These variables define the Redis hosts and ports we'll be using.</p>
<h2 id="heading-step-2-setting-up-redis-instances">Step 2: Setting Up Redis Instances</h2>
<p>We'll use Docker to simulate a distributed environment with multiple Redis instances.</p>
<p>Run the following commands to start three Redis containers:</p>
<pre><code class="lang-javascript">docker run -p <span class="hljs-number">6379</span>:<span class="hljs-number">6379</span> --name redis1 -d redis
docker run -p <span class="hljs-number">6380</span>:<span class="hljs-number">6379</span> --name redis2 -d redis
docker run -p <span class="hljs-number">6381</span>:<span class="hljs-number">6379</span> --name redis3 -d redis
</code></pre>
<p>This will set up three Redis instances running on different ports. We'll use these instances to implement <strong>consistent hashing</strong> and sharding.</p>
<h2 id="heading-step-3-implementing-the-url-shortener-service">Step 3: Implementing the URL Shortener Service</h2>
<p>Let's create our main application file, <strong>index.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> redis = <span class="hljs-built_in">require</span>(<span class="hljs-string">'redis'</span>);
<span class="hljs-keyword">const</span> shortid = <span class="hljs-built_in">require</span>(<span class="hljs-string">'shortid'</span>);

<span class="hljs-keyword">const</span> app = express();
app.use(express.json());

<span class="hljs-keyword">const</span> redisClients = [
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_1, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_1 }),
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_2, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_2 }),
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_3, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_3 })
];

<span class="hljs-comment">// Hash function to distribute keys among Redis clients</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRedisClient</span>(<span class="hljs-params">key</span>) </span>{
  <span class="hljs-keyword">const</span> hash = key.split(<span class="hljs-string">''</span>).reduce(<span class="hljs-function">(<span class="hljs-params">acc, char</span>) =&gt;</span> acc + char.charCodeAt(<span class="hljs-number">0</span>), <span class="hljs-number">0</span>);
  <span class="hljs-keyword">return</span> redisClients[hash % redisClients.length];
}

<span class="hljs-comment">// Endpoint to shorten a URL</span>
app.post(<span class="hljs-string">'/shorten'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { url } = req.body;
  <span class="hljs-keyword">if</span> (!url) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).send(<span class="hljs-string">'URL is required'</span>);

  <span class="hljs-keyword">const</span> shortId = shortid.generate();
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  <span class="hljs-keyword">await</span> redisClient.set(shortId, url);
  res.json({ <span class="hljs-attr">shortUrl</span>: <span class="hljs-string">`http://localhost:<span class="hljs-subst">${process.env.PORT}</span>/<span class="hljs-subst">${shortId}</span>`</span> });
});

<span class="hljs-comment">// Endpoint to retrieve the original URL</span>
app.get(<span class="hljs-string">'/:shortId'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { shortId } = req.params;
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  redisClient.get(shortId, <span class="hljs-function">(<span class="hljs-params">err, url</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err || !url) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).send(<span class="hljs-string">'URL not found'</span>);
    }
    res.redirect(url);
  });
});

app.listen(process.env.PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${process.env.PORT}</span>`</span>);
});
</code></pre>
<p>As you can see in this code, we have:</p>
<ol>
<li><p><strong>Consistent Hashing</strong>:</p>
<ul>
<li><p>We distribute keys (shortened URLs) across multiple Redis clients using a simple hash function.</p>
</li>
<li><p>The hash function ensures that URLs are distributed evenly across the Redis instances.</p>
</li>
</ul>
</li>
<li><p><strong>URL Shortening</strong>:</p>
<ul>
<li><p>The <strong>/shorten</strong> endpoint accepts a long URL and generates a short ID using the <strong>shortid</strong> library.</p>
</li>
<li><p>The shortened URL is stored in one of the Redis instances using our hash function.</p>
</li>
</ul>
</li>
<li><p><strong>URL Redirection</strong>:</p>
<ul>
<li><p>The <strong>/:shortId</strong> endpoint retrieves the original URL from the cache and redirects the user.</p>
</li>
<li><p>If the URL is not found in the cache, a <strong>404</strong> response is returned.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-step-4-implementing-cache-invalidation">Step 4: Implementing Cache Invalidation</h2>
<p>In a real-world application, URLs may expire or change over time. To handle this, we need to implement <strong>cache invalidation</strong>.</p>
<h3 id="heading-adding-expiry-to-cached-urls">Adding Expiry to Cached URLs</h3>
<p>Let's modify our <strong>index.js</strong> file to set an expiration time for each cached entry:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Endpoint to shorten a URL with expiration</span>
app.post(<span class="hljs-string">'/shorten'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { url, ttl } = req.body; <span class="hljs-comment">// ttl (time-to-live) is optional</span>
  <span class="hljs-keyword">if</span> (!url) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).send(<span class="hljs-string">'URL is required'</span>);

  <span class="hljs-keyword">const</span> shortId = shortid.generate();
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  <span class="hljs-keyword">await</span> redisClient.set(shortId, url, <span class="hljs-string">'EX'</span>, ttl || <span class="hljs-number">3600</span>); <span class="hljs-comment">// Default TTL of 1 hour</span>
  res.json({ <span class="hljs-attr">shortUrl</span>: <span class="hljs-string">`http://localhost:<span class="hljs-subst">${process.env.PORT}</span>/<span class="hljs-subst">${shortId}</span>`</span> });
});
</code></pre>
<ul>
<li><p><strong>TTL (Time-To-Live)</strong>: We set a default expiration time of <strong>1 hour</strong> for each shortened URL. You can customize the TTL for each URL if needed.</p>
</li>
<li><p><strong>Cache Invalidation</strong>: When the TTL expires, the entry is automatically removed from the cache.</p>
</li>
</ul>
<h2 id="heading-step-5-monitoring-cache-metrics">Step 5: Monitoring Cache Metrics</h2>
<p>To monitor <strong>cache hits</strong> and <strong>misses</strong>, we’ll add some logging to our endpoints in <strong>index.js</strong>:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/:shortId'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { shortId } = req.params;
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  redisClient.get(shortId, <span class="hljs-function">(<span class="hljs-params">err, url</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err || !url) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Cache miss for key: <span class="hljs-subst">${shortId}</span>`</span>);
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).send(<span class="hljs-string">'URL not found'</span>);
    }
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Cache hit for key: <span class="hljs-subst">${shortId}</span>`</span>);
    res.redirect(url);
  });
});
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ul>
<li><p><strong>Cache Hits</strong>: If a URL is found in the cache, it’s a cache hit.</p>
</li>
<li><p><strong>Cache Misses</strong>: If a URL is not found, it’s a cache miss.</p>
</li>
<li><p>This logging will help you monitor the performance of your distributed cache.</p>
</li>
</ul>
<h2 id="heading-step-6-testing-the-application">Step 6: Testing the Application</h2>
<ol>
<li><strong>Start your Redis instances</strong>:</li>
</ol>
<pre><code class="lang-javascript">docker start redis1 redis2 redis3
</code></pre>
<ol start="2">
<li><strong>Run the Node.js server</strong>:</li>
</ol>
<pre><code class="lang-javascript">node index.js
</code></pre>
<ol start="3">
<li><p><strong>Test the endpoints</strong> using <code>curl</code> or Postman:</p>
<ul>
<li><p>Shorten a URL:</p>
<pre><code class="lang-javascript">  POST http:<span class="hljs-comment">//localhost:3000/shorten</span>
  Body: { <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://example.com"</span> }
</code></pre>
</li>
<li><p>Access the shortened URL:</p>
<pre><code class="lang-javascript">  GET http:<span class="hljs-comment">//localhost:3000/{shortId}</span>
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</h2>
<p>Congratulations! You’ve successfully built a scalable <strong>URL shortener service</strong> with <strong>distributed caching</strong> using Node.js and Redis. Throughout this tutorial, you’ve learned how to:</p>
<ol>
<li><p>Implement <strong>consistent hashing</strong> to distribute cache entries across multiple Redis instances.</p>
</li>
<li><p>Optimize your application with <strong>cache invalidation strategies</strong> to keep data up-to-date.</p>
</li>
<li><p>Use <strong>Docker</strong> to simulate a distributed environment with multiple Redis nodes.</p>
</li>
<li><p>Monitor <strong>cache hits and misses</strong> to optimize performance.</p>
</li>
</ol>
<h3 id="heading-next-steps">Next Steps:</h3>
<ul>
<li><p><strong>Add a Database</strong>: Store URLs in a database for persistence beyond the cache.</p>
</li>
<li><p><strong>Implement Analytics</strong>: Track click counts and analytics for shortened URLs.</p>
</li>
<li><p><strong>Deploy to the Cloud</strong>: Deploy your application using Kubernetes for auto-scaling and resilience.</p>
</li>
</ul>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Caching vs Content Delivery Networks – What's the Difference? ]]>
                </title>
                <description>
                    <![CDATA[ By Anamika Ahmed In the world of network optimization, Content Delivery Networks (CDNs) and caching play a vital role in improving website performance and user experience.  And while both aim to speed up website loading times, they have distinct purp... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/caching-vs-content-delivery-network/</link>
                <guid isPermaLink="false">66d45d99680e33282da25e0c</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ content delivery network  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 01 Mar 2024 19:27:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Conducting-Research-Projects-Educational-Presentation-in-Pink-and-Yellow-Colorful-Line-Style-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Anamika Ahmed</p>
<p>In the world of network optimization, Content Delivery Networks (CDNs) and caching play a vital role in improving website performance and user experience. </p>
<p>And while both aim to speed up website loading times, they have distinct purposes and mechanisms. </p>
<p>In this tutorial, we'll dive deep into the details of CDNs and caching to understand their similarities, differences, and how they contribute to enhancing online experiences.</p>
<h3 id="heading-heres-what-well-cover">Here's what we'll cover:</h3>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-caching">What is Caching?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-a-content-delivery-network-cdn">What is a Content Delivery Network (CDN)?</a></li>
<li><a class="post-section-overview" href="#heading-caching-vs-cdns-whats-the-difference">Caching vs CDNs – What's the Difference?</a></li>
<li><a class="post-section-overview" href="#heading-when-to-use-caching">When to Use Caching</a></li>
<li><a class="post-section-overview" href="#heading-when-to-use-cdns">When to use CDNs</a></li>
<li><a class="post-section-overview" href="#heading-combining-caching-and-cdns">Combining Caching and CDNs</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-what-is-caching">What is Caching?</h2>
<p>Imagine you’re a librarian managing a popular library. Every day, readers come in asking for the same set of books like “Think and Grow Rich” or “The Intelligent Investor.” </p>
<p>Initially, you fetch these books from the main shelves, which takes time and effort. But soon, you notice a pattern: the same set of books are requested repeatedly by different readers. So, what do you do?</p>
<p>You decide to create a special section near the entrance where you keep copies of these frequently requested books. Now, when readers come asking for them, you don’t have to run to the main shelves each time. Instead, you simply hand them the copies from the special section, saving time and making the process more efficient. </p>
<p>This special section represents the cache, storing frequently accessed books for quick retrieval.</p>
<p>Caching is a technique used to store copies of frequently accessed data temporarily. The cached data can be anything from web pages and images to database query results. When a user requests cached content, the server retrieves it from the cache instead of generating it anew, significantly reducing response times.</p>
<p>When a web server receives a request, it can follow different caching strategies to handle it efficiently. One prevalent strategy is known as read-through caching:</p>
<ol>
<li>Request Received: The web server gets a request from a client.</li>
<li>Check Cache: It first looks into the cache to see if the response to the request is already there.</li>
<li>Cache Hit: If the response is in the cache (hit), it sends the data back to the client right away.</li>
<li>Cache Miss: If the response isn’t in the cache (miss), the server queries the database to fetch the required data.</li>
<li>Store in Cache: Once it gets the data from the database, it stores the response in the cache for future requests.</li>
<li>Send Response: Finally, the server sends the data back to the client.</li>
</ol>
<h3 id="heading-what-to-consider-when-implementing-a-cache-system">What to Consider When Implementing a Cache System</h3>
<h4 id="heading-decide-when-to-use-a-cache">Decide When to Use a Cache:</h4>
<ul>
<li>A cache is best for frequently read but infrequently modified data.</li>
<li>Cache servers are not suitable for storing critical data as they use volatile memory.</li>
<li>Important data should be stored in persistent data stores to prevent loss in case of cache server restarts.</li>
</ul>
<h4 id="heading-set-an-expiration-policy">Set an Expiration Policy:</h4>
<ul>
<li>Implement an expiration policy to remove expired data from the cache.</li>
<li>Avoid setting expiration dates too short (to prevent frequent database reloads), and too long (to prevent stale data).</li>
</ul>
<h4 id="heading-maintain-synchronization-between-data-stores-and-cache">Maintain Synchronization Between Data Stores and Cache</h4>
<ul>
<li>Inconsistencies can arise due to separate operations on data storage and cache, especially in distributed environments.</li>
</ul>
<h4 id="heading-mitigate-failures">Mitigate Failures:</h4>
<ul>
<li>Use multiple cache servers across different data centers to avoid single points of failure.</li>
<li>Over-provision memory to accommodate increased usage and prevent performance issues.</li>
</ul>
<h4 id="heading-implement-an-eviction-policy">Implement an Eviction Policy:</h4>
<ul>
<li>When the cache is full, new items may cause existing ones to be removed (cache eviction).</li>
<li>A popular eviction policy is Least Recently Used (LRU), but other policies like Least Frequently Used (LFU) or First In, First Out (FIFO) can be chosen based on specific use cases.</li>
</ul>
<h3 id="heading-real-world-applications-of-caching">Real-World Applications of Caching</h3>
<p><strong>Social Media Platforms:</strong> Imagine scrolling through your Facebook feed. Thanks to caching, you see profile pictures, trending posts, and recently liked content instantly, even if millions of users are accessing the platform simultaneously. </p>
<p>Caching these frequently accessed elements on servers or your device minimizes delays and makes the experience smoother and more engaging.</p>
<p><strong>E-commerce Websites:</strong> When browsing Amazon for a new gadget, you expect a seamless shopping experience. Caching plays a crucial role here. Product images, descriptions, and pricing information are cached, enabling the website to display search results and product pages rapidly. </p>
<p>This is especially crucial during peak seasons like Black Friday or Cyber Monday, where caching helps handle surges in traffic and ensures customers can complete their purchases without encountering delays.</p>
<p><strong>Content Management Systems (CMS):</strong> Millions of websites rely on CMS platforms like WordPress. To ensure smooth performance for all these users, many CMS platforms integrate caching plugins. These plugins cache frequently accessed pages, reducing the load on the server and database. </p>
<p>This translates to faster page loading times, improved SEO ranking due to faster indexing by search engines, and a more responsive website overall, providing a better experience for visitors.</p>
<h2 id="heading-what-is-a-content-delivery-network-cdn">What is a Content Delivery Network (CDN)?</h2>
<p>Now, think of a CDN as a global network of book delivery trucks. Instead of storing all the books in one central library, you have local branches worldwide, each with copies of the most popular books. </p>
<p>When readers request a book, you don’t have to ship it from the main library. Instead, you direct them to the nearest branch, where they can quickly pick up a copy. This cuts down on travel time (data transfer time) and keeps everyone happy with fast access to their favorite books.</p>
<p>In technical terms, a CDN is a network of servers distributed across various locations globally. Its primary purpose is to deliver web content, such as images, videos, scripts, and stylesheets to users more efficiently by reducing the physical distance between the server and the user.</p>
<h3 id="heading-how-cdns-work">How CDNs Work:</h3>
<p>First, imagine that User A wants to see an image on a website. They click on a link provided by the CDN, like “<a target="_blank" href="https://mysite.cloudfront.net/logo.jpg">https://mywebsite.cloudfront.net/image.jpg</a>". This requests the image.</p>
<p>Then, if the image isn’t in the CDN’s storage (cache), the CDN fetches the image from the original source, like a web server or Amazon S3.</p>
<p>In response to that, the original source sends the image back to the CDN. It might include a Time-to-Live (TTL) header, indicating how long the image should stay cached.</p>
<p>Next, the the CDN stores the image and serves it to User A. It stays cached until the TTL expires.</p>
<p>Then let's say that user B requests the same image. At that point, the CDN checks if it’s still in the cache. If the image is still cached (TTL hasn’t expired), the CDN serves it from there (a hit). Otherwise (a miss), it fetches a fresh copy from the origin.</p>
<h3 id="heading-what-to-consider-when-implementing-a-cdn">What to Consider When Implementing a CDN</h3>
<ul>
<li><strong>Cost Management</strong>: CDNs charge for data transfers. It’s wise to cache frequently accessed content, but not everything.</li>
<li><strong>Cache Expiry</strong>: Set appropriate cache expiry times. Too long, and content might be stale. Too short, and it strains origin servers.</li>
<li><strong>CDN Fallback</strong>: Plan for CDN failures. Ensure your website can switch to fetching resources directly from the origin if needed.</li>
<li><strong>Invalidating Files</strong>: You can remove files from the CDN before they expire using various methods provided by CDN vendors.</li>
</ul>
<h3 id="heading-real-world-applications-of-a-cdn">Real-World Applications of a CDN</h3>
<p><strong>Video Streaming Services:</strong> Imagine you're in Sydney, Australia, craving to watch the latest season of your favorite show on Netflix. Without a CDN, the data would have to travel all the way from a server in, say, California, leading to buffering and frustrating delays. </p>
<p>But thanks to CDNs, Netflix caches popular content on edge servers closer to you, in Sydney or its surrounding region. This significantly reduces the distance the data needs to travel, ensuring smooth playback and an uninterrupted viewing experience, regardless of your location. </p>
<p>In fact, studies show that CDNs can <strong>reduce video startup time by up to 50%</strong>, making a significant difference in user satisfaction.</p>
<p><strong>Gaming Content Distribution:</strong> Gamers know the pain of waiting for massive game updates or DLC downloads. But companies like Steam and Epic Games leverage CDNs to make things faster. </p>
<p>These platforms cache game files, updates, and multiplayer assets on edge servers close to gaming communities. This means whether you're downloading a new game in New York or patching your favorite title in Tokyo, the data doesn't have to travel across continents. </p>
<p>Using CDNs can decrease download times quite a bit, leading to quicker access to the games you love and smoother multiplayer experiences with minimal lag.</p>
<p><strong>Global News Websites:</strong> Staying informed about global events shouldn't be hindered by slow loading times. Major news organizations like BBC News and The New York Times use CDNs to ensure their breaking news updates and multimedia content reach audiences worldwide instantly. </p>
<p>By caching critical information like articles, videos, and images on servers across different continents, CDNs enable news websites to deliver real-time updates quickly, keeping readers informed regardless of their location. </p>
<p>During major events or emergencies, this can be especially crucial, as evidenced by a case study where a news organization using a CDN reported a <strong>20% increase in website traffic without any performance issues</strong> during a breaking news event.</p>
<h2 id="heading-caching-vs-cdns-whats-the-difference">Caching vs CDNs – What's the Difference?</h2>
<h3 id="heading-similarities-between-caching-and-cdns">Similarities between caching and CDNs:</h3>
<p><strong>Improved Performance:</strong> Both CDNs and caching aim to enhance website performance by reducing latency and speeding up content delivery.</p>
<p><strong>Efficient Resource Utilization:</strong> By serving cached or replicated content, both approaches help optimize resource utilization and reduce server load.</p>
<p><strong>Enhanced User Experience:</strong> Faster load times lead to a better user experience, whether achieved through CDNs or caching.</p>
<h3 id="heading-differences-between-caching-and-cdns">Differences between Caching and CDNs</h3>
<h4 id="heading-scope">Scope:</h4>
<ul>
<li>CDNs: CDNs are a network of servers located in different geographic locations around the world.</li>
<li>Caching: Caching is a method of storing web content on a user’s local device or server.</li>
</ul>
<h4 id="heading-implementation">Implementation:</h4>
<ul>
<li>CDNs: CDNs require a separate infrastructure and configuration.</li>
<li>Caching: Caching can be implemented within a web application or server using caching rules and directives.</li>
</ul>
<h4 id="heading-geographic-coverage">Geographic Coverage:</h4>
<ul>
<li>CDNs: Designed to deliver web content to users across the world.</li>
<li>Caching: Typically used to improve performance for individual users or within a local network.</li>
</ul>
<h4 id="heading-network-architecture">Network Architecture:</h4>
<ul>
<li>CDNs: Use a distributed network of servers to cache and deliver content.</li>
<li>Caching: This can be implemented using various types of storage such as local disk, memory, or a server-side cache.</li>
</ul>
<h4 id="heading-performance-benefits">Performance Benefits:</h4>
<ul>
<li>CDNs: Provide faster and more reliable content delivery by caching content in multiple locations.</li>
<li>Caching: Improves performance by reducing the number of requests to the origin server and delivering content faster from a local cache.</li>
</ul>
<h4 id="heading-cost">Cost:</h4>
<ul>
<li>CDNs: Can be more expensive to implement and maintain due to the need for a separate infrastructure and ongoing costs for network maintenance.</li>
<li>Caching: Can be implemented using existing infrastructure and server resources, potentially reducing costs.</li>
</ul>
<h2 id="heading-when-to-use-caching">When to use Caching</h2>
<p>Caching is ideal for frequently accessed content that doesn't change frequently. This includes static assets like images, CSS files, and JavaScript libraries.</p>
<p>It's particularly effective for websites with a substantial user base accessing similar content, such as news websites, blogs, and e-commerce platforms.</p>
<p>Caching can also significantly reduce server load and improve response times for users, especially in scenarios where content delivery latency is a concern.</p>
<h2 id="heading-when-to-use-cdns">When to use CDNs</h2>
<p>CDNs are invaluable for delivering content to a global audience, especially when geographical distance between users and origin servers leads to latency issues.</p>
<p>They are well-suited for serving dynamic content, streaming media, and handling sudden spikes in traffic.</p>
<p>CDNs also excel in scenarios where content needs to be delivered reliably and consistently across diverse geographic regions, ensuring optimal user experience regardless of location.</p>
<h2 id="heading-combining-caching-and-cdns">Combining Caching and CDNs</h2>
<p>In many scenarios, employing both caching and CDNs together yields optimal results, particularly for dynamic websites and applications where a mix of static and dynamic content delivery is essential. Let's consider a popular news website as an example.</p>
<p>Imagine a bustling news website that regularly publishes breaking news articles, accompanied by images and videos. While the core news content is dynamic and frequently updated, the images and videos associated with older articles remain relatively static and are accessed repeatedly by users.</p>
<p>To address this, the website can implement a combined strategy:</p>
<ol>
<li><strong>Caching on the Origin Server:</strong> Frequently accessed elements like website templates, navigation menus, and static content are cached directly on the origin server. This caching reduces server load and enhances performance for initial page loads.</li>
<li><strong>CDN Caching:</strong> The website leverages a CDN to cache frequently accessed images and videos associated with news articles on edge servers located worldwide. This ensures that users, regardless of their geographic location, can swiftly access these elements with minimal latency.</li>
</ol>
<p>There are many benefits of the combined approach, such as:</p>
<ul>
<li><strong>Faster Loading Times:</strong> By serving cached content from both the origin server and CDN edge servers, users experience significantly faster loading times, leading to a more engaging browsing experience.</li>
<li><strong>Reduced Server Load:</strong> Caching alleviates pressure on the origin server, enabling it to efficiently process dynamic content updates while serving static elements from cache.</li>
<li><strong>Improved Global Reach:</strong> The CDN ensures that users worldwide can access the website and its content with minimal delays, irrespective of their proximity to the origin server.</li>
</ul>
<p>But there are also some factors to consider:</p>
<ul>
<li><strong>Cache Invalidation:</strong> Regularly updating cached content ensures users access the latest information. Most CDNs offer efficient cache invalidation mechanisms to facilitate this process.</li>
<li><strong>Cost Optimization:</strong> While combining caching and CDNs enhances performance, it's crucial to evaluate the cost-effectiveness of caching specific content. Analyzing user access patterns helps determine the optimal caching strategy.</li>
</ul>
<p>By strategically combining caching and CDNs, you and your team can create a robust content delivery infrastructure that delivers a superior user experience worldwide.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Both CDNs and caching play crucial roles in optimizing website performance and user experience by speeding up content delivery. </p>
<p>While caching stores frequently accessed data locally for quick retrieval, CDNs provide a geographically distributed network of servers to deliver content efficiently to users worldwide. </p>
<p>Understanding their similarities in performance improvement and resource utilization, as well as their key differences in scope, implementation, and cost is crucial for choosing the right approach for your specific needs.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Caching in React – How to Use the useMemo and useCallback Hooks ]]>
                </title>
                <description>
                    <![CDATA[ By Scott Gary As you become more proficient at coding in React, performance will become a major focal point in your development process.  As with any tool or programming methodology, caching plays a huge role when it comes to optimizing React applica... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/caching-in-react/</link>
                <guid isPermaLink="false">66d460eea326133d12440a76</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react hooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 15 May 2023 18:39:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/caching-react.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Scott Gary</p>
<p>As you become more proficient at coding in React, performance will become a major focal point in your development process. </p>
<p>As with any tool or programming methodology, caching plays a huge role when it comes to optimizing React applications.</p>
<p>Caching in React typically goes by the term <em>memoization</em>. It's used to improve performance by reducing the amount of times a component renders due to state or prop mutations.</p>
<p>React provides two APIs for caching: useMemo and useCallback. useCallback is a hook that memoizes a function, while useMemo is a hook that memoizes a value. These two hooks are often used in conjunction with the Context API to further improve efficiency.</p>
<p>Here’s a basic list of topics we’ll be covering in this article:</p>
<ol>
<li>React caching default behavior.</li>
<li>The useMemo hook.</li>
<li>The useCallback hook.</li>
</ol>
<p>In order to follow along, you'll need a decent understanding of React and stateful components.</p>
<h2 id="heading-default-caching-behavior-in-react">Default Caching Behavior in React</h2>
<p>By default, <a target="_blank" href="https://www.ohmycrawl.com/react/">React</a> uses a technique called “shallow comparison” to determine whether a component should be re-rendered. This basically means that if the props or state of a component haven’t changed, React will assume that the output of the component hasn’t changed either and won’t re-render it.</p>
<p>While this default caching behavior is very effective by itself, it isn’t always enough to optimize complex components that require advanced state management. </p>
<p>In order to achieve more control over your component’s caching and rendering behavior, React offers the <strong>useMemo</strong> and <strong>useCallback</strong> hooks.</p>
<h2 id="heading-caching-in-react-with-the-usememo-hook">Caching in React with the useMemo Hook</h2>
<p>useMemo is useful when you need to do an expensive computation to retrieve a value, and you want to ensure that the computation is only performed when necessary. By memoizing the value using useMemo, you can ensure that the value is only computed when its dependencies change.</p>
<p>In a React component, you may have multiple properties that make up your state. If a piece of state changes that has nothing to do with our expensive value, why recompute it if it hasn’t changed?</p>
<p>Here’s an example code block reflecting a basic useMemo implementation:</p>
<pre><code>react
<span class="hljs-keyword">import</span> React, { useState, useMemo } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Example</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">const</span> [txt, setTxt] = useState(“Some text”);
<span class="hljs-keyword">const</span> [a, setA] = useState(<span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> [b, setB] = useState(<span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> sum = useMemo(<span class="hljs-function">() =&gt;</span> {
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Computing sum...'</span>);
<span class="hljs-keyword">return</span> a + b;
}, [a, b]);
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Text: {txt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>a: {a}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>b: {b}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>sum: {sum}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setTxt(“New Text!”)}&gt;Set Text<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setA(a + 1)}&gt;Increment a<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setB(b + 1)}&gt;Increment b<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);
}
</code></pre><p>In our Example component above, assume the <strong>sum()</strong> function performs an expensive computation. If the <strong>txt</strong> state is updated, React is going to re-render our component, but because we memoized the returned value of sum, this function will not run again at this time.</p>
<p>The only time the <strong>sum()</strong> function will run is if either the <strong>a</strong> or <strong>b</strong> state has been mutated (changed). This is an excellent improvement upon the default behavior, which will rerun this method upon each re-render.</p>
<h2 id="heading-caching-in-react-with-the-usecallback-hook">Caching in React with the useCallback Hook</h2>
<p>useCallback is useful when you need to pass a function as a prop to a child component, and you want to ensure that the function reference does not change unnecessarily. By memoizing the function using useCallback, you can ensure that the function reference remains the same as long as its dependencies do not change.</p>
<p>Without getting too heavy into JavaScript function references, let’s just take a look at how they can affect the rendering of your React app. When a function reference changes, any child components that receive the function as a prop will re-render, even if the function logic itself has not changed.</p>
<p>This is because, as we already mentioned, React does a shallow comparison of prop values to determine whether a component should re-render, and a new function reference will always be considered a different value than the previous one.</p>
<p>In other words, the simple act of redeclaring a function (even the same exact function), causes the reference to change, and will cause the child component that receives the function as a prop to unnecessarily re-render.</p>
<p>Here’s an example code block reflecting a basic useCallback implementation:</p>
<pre><code>react
<span class="hljs-keyword">import</span> React, { useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChildComponent</span>(<span class="hljs-params">{ onClick }</span>) </span>{
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'ChildComponent is rendered'</span>);
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>&gt;</span>Click me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
);
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Example</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> [txt, setTxt] = useState(“Some text…”);
<span class="hljs-keyword">const</span> incrementCount = useCallback(<span class="hljs-function">() =&gt;</span> {
setCount(<span class="hljs-function"><span class="hljs-params">prevCount</span> =&gt;</span> prevCount + <span class="hljs-number">1</span>);
}, [setCount]);
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Text: {txt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Count: {count}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{setTxt}</span>&gt;</span>Set Text<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{setCount}</span>&gt;</span>Increment<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">ChildComponent</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{incrementCount}</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);
}
</code></pre><p>As you can see in the above example, we pass the <strong>incrementCount</strong> method instead of the <strong>setCount</strong> method to the child component. This is because <strong>incrementCount</strong> is memoized, and when we run our <strong>setTxt</strong> method, it won’t cause the child component to unnecessarily re-render.</p>
<p>The only way our child component will re-render in this example is if the <strong>setCount</strong> method runs, because we passed it as a dependency parameter to our <strong>useCallback</strong> memoization.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Caching is an important technique for optimizing React applications. By reducing unnecessary re-renders, caching can help to improve the performance and efficiency of your application.</p>
<p>React provides a default caching behavior by using a virtual DOM to compare changes in state and props, and only updating components after a shallow comparison reflects changes. This is a great optimization technique that’s sufficient in many scenarios, but sometimes more fine-grained control is desired.</p>
<p>The useMemo and useCallback hooks were created to achieve this fine-grained control. </p>
<p>useMemo is used to memoize the <em>results</em> of a function call, and is useful when the function is expensive to compute and the result does not change often. </p>
<p>useCallback is used to memoize the actual reference of a function rather than the returned value, and is used when the function is passed as a prop to child components that might cause unnecessary re-renders.</p>
<p>Want to learn more? To learn more check out the <a target="_blank" href="https://www.ohmycrawl.com/blog/">OhMyCrawl Blog</a> for more programming tips for SEO.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Pre-Caching? How to Increase Website Speed and Performance ]]>
                </title>
                <description>
                    <![CDATA[ By Saurabh Dashora Speed and performance are two of the key ingredients that make a website stand out from its peers. Imagine visiting a bestseller list on the Amazon website and finding that the product pages take forever to show up. What about a bl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-detailed-guide-to-pre-caching/</link>
                <guid isPermaLink="false">66d460ee3a8352b6c5a2aaf9</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 19 Jan 2023 18:05:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/pre-caching.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Saurabh Dashora</p>
<p><strong>Speed</strong> and <strong>performance</strong> are two of the key ingredients that make a website stand out from its peers.</p>
<p>Imagine visiting a bestseller list on the Amazon website and finding that the product pages take forever to show up.</p>
<p>What about a blog publishing some great stories that readers aren’t able to read because the incoming traffic exceeds the server’s capacity?</p>
<p>And if that’s not enough, just measure your frustration when there is a much-anticipated new movie on Netflix and all you get to see is the loading screen.</p>
<p>If you are the one running such a website, this is a <strong>good problem</strong> to have. Clearly, people are enthusiastic about the things you are offering and there is a disproportionate amount of traffic for certain pages.</p>
<p>However, if not handled properly, the situation can quickly spiral into chaos and end up alienating your users. Ultimately, it will result in a loss of business whether in terms of viewership time, sales, or even general user goodwill.</p>
<p><strong>So how can you avoid this situation?</strong></p>
<p>One of the most prominent techniques to boost website speed and performance is <strong>pre-caching</strong>.</p>
<p>In this post, you will get to understand the concept of pre-caching in great detail.</p>
<h2 id="heading-what-is-pre-caching">What is Pre-caching?</h2>
<p>Pre-caching is a technique used to <strong>proactively</strong> store or cache data in anticipation of future requests. The idea is to cache commonly accessed data or resources in advance so that when the time comes, you can deliver it to the end-user faster.</p>
<p>Check out the below illustration that depicts how pre-caching can work in an overall system context.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-133.png" alt="pre-caching in a system" width="600" height="400" loading="lazy">
<em>How pre-caching works</em></p>
<p>You can perform pre-caching on the <strong>client-side</strong> such as in the web browser. Alternatively, you can also do it on the <strong>server-side</strong> using Content Delivery Networks (CDNs) or other caching solutions.</p>
<p>Whatever may be the approach, the goal of pre-caching is to <strong>improve the performance and user experience by reducing content load times</strong>.</p>
<h2 id="heading-how-pre-caching-works">How Pre-caching Works</h2>
<p>The process of pre-caching requires you to store a copy of the data in a location that’s closer to the user or store the data in advance so that it is readily available when needed.</p>
<p>Below are the high-level steps to implement pre-caching.</p>
<ul>
<li>First, <strong>identify the data or resources</strong> that are accessed most frequently. These resources are good candidates for pre-caching. For example, most popular blog posts, bestseller product list, and so on. You could also include images, JS files, and stylesheets to the pre-caching list</li>
<li>After the identification, you need to <strong>decide on the caching system</strong> to store the pre-cached data. This could be a local cache on the user’s device or even a distributed cache spanning multiple servers. The choice will depend on the type of resource.</li>
<li>Next, you need to <strong>pre-populate the cache</strong> with the identified resources. This step can be performed automatically by the system during initialization phase. Alternatively, you can do it on an on-demand basis as the data is accessed by the users. Remember, pre-caching is all about being proactive.</li>
<li>Once the setup is ready, you can let your system do the job. Whenever the system needs to access the pre-cached data, it can retrieve directly from the cache instead of fetching from the slower external source.</li>
</ul>
<h2 id="heading-how-to-decide-what-to-pre-cache">How to Decide What to Pre-cache</h2>
<p>The success of pre-caching depends on the <strong>quality of data</strong> that is pre-cached. It is important for you to choose the right data to cache.</p>
<p>While it can sound daunting in practice, you can follow the below rules to make the right choice:</p>
<ul>
<li><strong>Prioritize critical resources</strong> such as the HTML, CSS, and JavaScript files that are needed for the initial page load. Usually, these are the most important resources that are required to provide a fast and smooth user experience.</li>
<li>You should also consider <strong>caching third-party resources</strong> like fonts, libraries or scripts from other domains. These resources can be pre-cached locally to reduce frequent network requests.</li>
<li>Your initial assumption about the best resources for pre-caching can change. Therefore, it is vital to <strong>perform a regular analysis</strong> of your web application’s usage pattern and derive insights about the user activity. This will help your pre-caching catalog stay relevant as your application evolves.</li>
<li>Explore the <strong>use of machine learning</strong> for pre-caching. You can <strong>build prediction models</strong> in order to predict which resources will be requested in the future based on past usage patterns. You can train this model on historical data and use it to identify the best candidate resources for pre-caching. Of course, this is a costly approach and its use depends on the importance of pre-caching in your application context.</li>
</ul>
<h2 id="heading-the-advantages-of-pre-caching">The Advantages of Pre-caching</h2>
<p>Pre-caching may sound like a lot of trouble. Why bother worrying about it?</p>
<p>In my view, the advantages of pre-caching can outweigh the difficulties. Here are a few important benefits of pre-caching:</p>
<ul>
<li><strong>Performance Improvement</strong> – When you pre-cache data, you are essentially reducing the load time for the content. This leads to a faster user experience. Websites and apps that expect a high volume of traffic or deal with huge amounts of data are significantly benefited by this.</li>
<li><strong>Improved user experience</strong> – Who doesn’t like a good user experience? Faster load times improve the overall user experience and help reduce the bounce-rate (percentage of users leaving a website after visiting one page). Pre-caching also improves content availability even in the case of poor network connection.</li>
<li><strong>Cost Reduction</strong> – Pre-caching can help you reduce costs. For example, if you pre-cache data on a CDN, you are ultimately reducing the load on the origin server. This saves bandwidth and reduces the server cost.</li>
<li><strong>Offline Access</strong> – With pre-caching, you can also enable offline access to content using the concept of service workers. This is extremely important for mobile apps and websites that need to work in areas with poor internet connectivity.</li>
<li><strong>Security</strong> – Though it is an indirect benefit, you also improve the overall security of your assets using pre-caching. Basically, pre-caching blunts the impact of DoS (Denial of Service) attacks since the application won’t have to serve the pre-cached resources from the origin server.</li>
</ul>
<h2 id="heading-how-to-implement-pre-caching-in-nodejs">How to Implement Pre-caching in Node.js</h2>
<p>Let’s look at a very basic implementation of <strong>pre-caching in Node.js</strong>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> nodecache = <span class="hljs-built_in">require</span>(<span class="hljs-string">'node-cache'</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">'isomorphic-fetch'</span>);

<span class="hljs-comment">//Setting up Express</span>
<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">//Creating the node-cache instance</span>
<span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> nodecache({<span class="hljs-attr">stdTTL</span>: <span class="hljs-number">10</span>})

<span class="hljs-comment">//We are using the fake API available at &lt;https://jsonplaceholder.typicode.com/&gt;</span>
<span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">'&lt;https://jsonplaceholder.typicode.com/posts/&gt;'</span>;

<span class="hljs-comment">//Pre-caching Popular Posts</span>
[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].map(<span class="hljs-keyword">async</span> (id) =&gt; {
    <span class="hljs-keyword">const</span> fakeAPIURL = baseURL + id
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetch(fakeAPIURL).then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> response.json());
    cache.set(id, data);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Post Id <span class="hljs-subst">${id}</span> cached`</span>);
})

<span class="hljs-comment">//API Endpoint to demonstrate caching</span>
app.get(<span class="hljs-string">'/posts/:id'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">const</span> id = req.params.id;
    <span class="hljs-keyword">if</span> (cache.has(id)) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetching data from the Node Cache'</span>);
        res.send(cache.get(id));
    }
    <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">const</span> fakeAPIURL = baseURL + id
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetch(fakeAPIURL).then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> response.json());

        cache.set(req.params.id, data);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetching Data from the API'</span>);
        res.send(data);
    }
})

<span class="hljs-comment">//Starting the server</span>
app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The server is listening on port 3000'</span>)
})
</code></pre>
<p>The example uses the <code>node-cache</code> library to create an in-memory cache. It is borrowed from a blog post that shows <a target="_blank" href="https://progressivecoder.com/in-memory-caching-nodejs/">how to implement an in-memory cache in Node.js</a>.</p>
<p>To simulate how pre-caching works, it assumes that posts 1, 2, and 3 are extremely popular posts and suitable candidates for pre-caching. Therefore, the data for these posts are pre-fetched during the application startup process and stored in the <code>cache</code> object.</p>
<p>When a request is made for these specific posts, the application fetches the data directly from the cache instead of calling the API.</p>
<p>Of course, this is a very basic setup for pre-caching. But you should get the idea of the concept in action.</p>
<h2 id="heading-types-of-pre-caching">Types of Pre-caching</h2>
<p>While the previous section’s example demonstrated a particular approach to implement pre-caching, there are other methods as well.</p>
<p>Since the basic idea of pre-caching is quite simple, you can implement it in several different ways. Broadly, there are two main approaches: client-side pre-caching and server-side pre-caching.</p>
<h3 id="heading-client-side-pre-caching">Client-side Pre-caching</h3>
<p>The most common way of client-side pre-caching is to proactively cache resources on the browser. With <strong>browser-based caching</strong>, you are trying to anticipate the resources that will be requested and store them in advance using the browser’s cache-storage API.</p>
<p><strong>Browser-based caching</strong> often relies on the caching headers to determine if a particular resource is cacheable. When a user requests a page, the browser checks its cache to see if a copy of the requested data is already available. If it is, the browser loads the data from the cache. This reduces the time it takes to load the page.</p>
<p>Another way of implementing browser-based caching is using the <strong>Service Worker API.</strong> Basically, assets are pre-cached ahead of time, usually during the process of installing a service worker.</p>
<p>With service worker pre-caching, key static assets and materials such as HTML, CSS, JS and image files that are necessary for offline access get downloaded and stored in a <code>Cache</code> instance. You can use the JavaScript library named <strong><a target="_blank" href="https://developer.chrome.com/docs/workbox/">Workbox</a></strong> that makes it easy to precache resources and provides a simple API for working with the service worker cache.</p>
<h3 id="heading-server-side-pre-caching">Server-Side Pre-caching</h3>
<p>The second approach is <strong>server-side pre-caching</strong>. You can also do it using multiple ways.</p>
<p>First method is to use <strong>Content Delivery Networks or CDNs</strong> that store a copy of the data on servers that are distributed around the world. When a user makes a request for some data, it is delivered from the server that is closest to the user. This reduces the time to handle the request and makes your website faster for the user.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-134.png" alt="pre-caching with content delivery networks" width="600" height="400" loading="lazy">
<em>Pre-caching with CDN</em></p>
<p>Second approach is to use a <strong>Caching Proxy Server</strong>. It is a server that sits in front of the origin server and works as a caching layer by storing a copy of the data. Next time there is a request from the user, the proxy server delivers the data directly without having to make a request to the origin server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-135.png" alt="caching proxy server" width="600" height="400" loading="lazy">
<em>Caching Proxy Server</em></p>
<p>Here’s a sample configuration to use NGINX as a caching proxy server to cache all files with extension jpeg/jpg, png, css and js for 60 seconds.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># configure the proxy server to cache all assets for 1 hour</span>
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_cache:20m inactive=60m;

<span class="hljs-comment"># set the cache control header to a max-age of 1 hour</span>
add_header Cache-Control <span class="hljs-string">"public, max-age=3600"</span>;

<span class="hljs-comment"># cache all assets</span>
location ~* \\.(jpg|jpeg|png|css|js)$ {
    proxy_cache static_cache;
    proxy_cache_valid 200 60m;
    proxy_pass http://origin_server;
}
</code></pre>
<h2 id="heading-pre-caching-best-practices">Pre-Caching Best Practices</h2>
<p>Pre-caching is an extremely useful technique to improve your web application’s performance. However, you should follow some best practices to ensure that pre-caching gives the desired results.</p>
<p>Here are a few best practices you should keep in mind while pre-caching:</p>
<ul>
<li><strong>Cache only necessary resources.</strong> Caching too many resources can lead to wasting storage space and nullifying the advantages of pre-caching. You should only cache the resources that have the greatest impact on performance improvement.</li>
<li><strong>Don’t forget to version pre-cached resources.</strong> Even pre-cached resources may get updated in the future. So you should make sure to use versioning to identify the latest version of a resource. When a resource gets updated, you should also increment the version number to keep track of subsequent updates.</li>
<li><strong>Use appropriate cache-control headers</strong>. Cache-control headers help us ensure that the resources are cached for the right amount of time. For example, an e-commerce platform might have a list of bestselling products that’s changing frequently due to products dropping off the charts or rising up. Such resources should have a shorter cache lifetime to keep the cache relevant.</li>
<li><strong>Use a Content Delivery Network (CDN)</strong>. CDNs help reduce the time it takes to load the resources. You should leverage a CDN to distribute resources to the edge servers located closer to the user. Edge servers in combination with pre-caching is a powerful technique to boost your web application’s performance.</li>
<li><strong>Use a library or framework to enable pre-caching</strong>. Even though you might be tempted to build your solutions for pre-caching from scratch, you should consider using a library such as Workbox to enable service workers for pre-caching resources. For server-side caching, consider using a combination of CDNs and caching proxy servers.</li>
</ul>
<h2 id="heading-drawbacks-of-pre-caching">Drawbacks of Pre-caching</h2>
<p>While pre-caching is a very useful technique and mostly beneficial, you should make sure to avoid the below drawbacks:</p>
<ul>
<li><strong>Stale data</strong>: With pre-caching, you are storing data in advance. This data may not always be up-to-date. If the data changes frequently, the pre-cached version will become stale and lead to issues. To avoid this situation, you must have a proper strategy for cache invalidation that can get rid of stale data.</li>
<li><strong>Inefficient use of resources</strong>: While pre-caching, you are basically assuming that the data you are caching will be accessed frequently. Such assumptions may <strong>not</strong> always be correct and you may end up with data that’s not needed frequently. If the size of such data grows beyond a certain limit, the caching solution can become inefficient and cause wastage of precious resources that can be used for other purposes.</li>
<li><strong>Limited scope</strong>: Pre-caching is limited in scope. It only applies to data that is known in advance and can be pre-populated in the cache. This is mostly static data. It’s tough to implement pre-caching for data that is generated dynamically without the use of complex algorithms.</li>
</ul>
<h2 id="heading-thats-it">That’s it</h2>
<p>To conclude, pre-caching is a powerful technique that can potentially improve the speed and performance of your website.</p>
<p>With pre-caching, you are essentially trying to anticipate which resources a user is likely to request next and downloading them in advance.</p>
<p>Even on its own, <strong>pre-caching is a game-changing technique</strong> that can have a significant impact on the user experience of your web application.</p>
<p>However, it's equally important to keep in mind that pre-caching is only one aspect of the website optimization process. You should try and use it in conjunction with other techniques such as minification, compression, and code optimization.</p>
<p>If you liked this post and found it useful, consider sharing it with friends and colleagues.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Cache Expensive Database Queries Using the Momento Serverless Cache ]]>
                </title>
                <description>
                    <![CDATA[ By Andrew Brown When to Use a Cache When you are building a web-application, you'll need to fetch data from a database. As your traffic and the size of your database grows, you will find that querying your database gets slower and slower. In order to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/serverless-caching-for-your-web-applications/</link>
                <guid isPermaLink="false">66d45db8680e33282da25e2f</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 22 Dec 2022 20:45:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/pexels-tiger-lily-4483610.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Andrew Brown</p>
<h2 id="heading-when-to-use-a-cache">When to Use a Cache</h2>
<p>When you are building a web-application, you'll need to fetch data from a database. As your traffic and the size of your database grows, you will find that querying your database gets slower and slower.</p>
<p>In order to return requests to users quickly, a cache can be a cost-effective and easy solution rather than having to upgrade your database.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screen-Shot-2022-12-22-at-10.37.53-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Diagram showing how a cache works</em></p>
<p>A cache is an in-memory database which can store simple data as a key and value data structure.</p>
<p>Popular open-source caching solutions that already exist are Memcache and Redis.</p>
<h2 id="heading-what-is-momento">What is Momento?</h2>
<p>Momento Serverless Cache is a Caching-as-a-Service (CaaS) that you can integrate as a caching solution. It will reduce expensive or unnecessary queries against your primary database.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screen-Shot-2022-12-22-at-12.54.33-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Momento has an SDK for the eight most popular programming languages. Here's an example of using the Ruby SDK to do a simple get and set of a cache item:</p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'momento'</span>

client = Momento::SimpleCacheClient.new(
  <span class="hljs-symbol">auth_token:</span> ENV[<span class="hljs-string">'MOMENTO_AUTH_TOKEN'</span>],
  <span class="hljs-symbol">default_ttl:</span> ENV[<span class="hljs-string">'MOMENTO_TTL'</span>]
)

response = client.set ENV[<span class="hljs-string">'MOMENTO_CACHE_NAME'</span>], <span class="hljs-string">"Hello"</span>, <span class="hljs-string">"World"</span>
response = client.get ENV[<span class="hljs-string">'MOMENTO_CACHE_NAME'</span>], key
<span class="hljs-keyword">if</span> response.hit?
  puts <span class="hljs-string">"Cache returned: <span class="hljs-subst">#{response.value_string}</span>"</span>
<span class="hljs-keyword">elsif</span> response.miss?
  puts <span class="hljs-string">"The item wasn't found in the cache."</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Just a quick note: the company is called Momento and the caching product is called the Momento Serverless Cache, but we'll just say "Momento" to refer to the product to keep it simple.</p>
<h2 id="heading-why-use-momento">Why Use Momento?</h2>
<p>Momento is a Serverless Cache, and as a result has the following benefits:</p>
<ul>
<li>Creating a new cache is nearly instant</li>
<li>You pay based on usage ($0.15/GB per transfer cost)</li>
<li>It has a very generous free-tier (first 50GB per month free)</li>
<li>No credit card required to start using the cache</li>
<li>It just scales, no server configuration or tuning required</li>
<li>It just works from anywhere</li>
</ul>
<p>Momento is ideal for developers who just need a simple caching solution, and want to focus on their code instead of having to mange caching infrastructure.</p>
<h2 id="heading-why-not-use-a-managed-open-source-service">Why Not Use a Managed Open Source Service?</h2>
<p>Their are already open-source managed cloud services.</p>
<p>For example:</p>
<ul>
<li>AWS has ElasticCache which allows you to run Memcached or Redis</li>
<li>Amazon MemoryDB for Redis</li>
<li>Azure has an Azure Cache for Redis</li>
<li>Redis has its own Redis Cloud offering</li>
</ul>
<p>These existing cloud services can simplify some aspects of hosting and scaling a caching layer for your web-applications. But there are some things to consider:</p>
<ul>
<li>you have to choose the right size compute</li>
<li>there are additional application integration steps</li>
<li>it takes time (up to an hour) to provision a cache</li>
<li>there are limitations on where a cache must live in your network</li>
</ul>
<p>The Redis opens-source in memory database, for example, has a variety of complex data structures and data operations. It could be suited to more advanced use cases, where it goes beyond being a cache and can act (and is marketed as) a primary database.</p>
<p>There is no wrong answer when choosing a cache. What you have are trade-offs and you need to choose the best solution for your use-case.</p>
<h2 id="heading-how-to-install-momento-cli">How to Install Momento CLI</h2>
<p>Momento (at the time of writing this article) is an API-only service. </p>
<p>So in order to use Momento you need to create an account by using their CLI tool.</p>
<p><strong>Windows Install Instructions:</strong></p>
<pre><code class="lang-bash">brew tap momentohq/tap
brew install momento-cli
</code></pre>
<p><strong>Linux Install Instructions:</strong></p>
<pre><code class="lang-bash">wget https://github.com/momentohq/momento-cli/releases/download/v0.22.8/momento-cli-0.22.8.linux_x86_64.tar.gz
tar -xvf momento-cli-0.22.8.linux_x86_64.tar.gz --strip-components 3
sudo mv momento /usr/<span class="hljs-built_in">local</span>/bin
rm momento-cli-0.22.8.linux_x86_64.tar.gz
</code></pre>
<p>Once installed, test that the CLI is working with the following command:</p>
<pre><code class="lang-bash">momento --version
&gt; momento 0.22.6
</code></pre>
<h2 id="heading-how-to-create-a-momento-account">How to Create a Momento Account</h2>
<p>To create an account, enter the following command: </p>
<pre><code class="lang-bash">momento account signup aws \
--email YOUR_EMAIL \
--region us-east-1

&gt; Signing up <span class="hljs-keyword">for</span> Momento...
&gt; Success! Your access token will be emailed to you shortly.
</code></pre>
<p>Remember to replace <code>YOUR_EMAIL</code> with your own email address (for example, andrew@example.com).</p>
<p>Momento is <strong>going to email an access token</strong> and this access token is how Momento will identify and authorize our future API calls to use the cache.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screen-Shot-2022-12-21-at-9.55.07-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example email with provided token</em></p>
<h3 id="heading-why-did-i-need-to-type-aws-when-creating-an-account">Why did I need to type "aws" when creating an account?</h3>
<p>Notice that we specified <strong>aws</strong> and the AWS region <strong>us-east-1</strong> on creation.</p>
<p>When you create an account, you need to say which Cloud Service Provider (CSP) that the cache will be hosted on. </p>
<p>You might think, do I need to have and connect my own AWS account? </p>
<p>The answer is no. The cache is being setup within Momento's AWS account. </p>
<p>The reason Momento allows you to choose the CSP is because some companies have data policies about what part of the world and what CSP their data must reside on. </p>
<h2 id="heading-how-to-configure-the-cli-to-use-the-access-token">How to Configure the CLI to Use the Access Token</h2>
<p>We need to configure the CLI to use the access token that was emailed.</p>
<p>Type the <code>momento configure</code> command to prompt the configuration wizard:</p>
<pre><code class="lang-bash">momento configure
Token: XXXXXXXXXXXXXXXXX
Default Cache [default-cache]: 
Default Ttl Seconds [600]: 
default-cache successfully created as the default with default TTL of 600s
</code></pre>
<ul>
<li><strong>Token</strong>: Enter the token by copying and pasting it from the previous email</li>
<li><strong>Default Cache</strong>: Hit enter</li>
<li><strong>Default TTL:</strong> Hit enter</li>
</ul>
<p>The <code>momento configure</code> will generate two TOML configuration files:</p>
<ol>
<li><code>~/.momento/credentials</code> – stores sensitive configuration, for example: access token</li>
</ol>
<pre><code class="lang-toml"><span class="hljs-section">[default]</span>
<span class="hljs-attr">token</span>=XXXXXXXX
</code></pre>
<ol start="2">
<li><code>~/.momento/config</code> – stores common configuration, for example: ttl default</li>
</ol>
<pre><code class="lang-toml"><span class="hljs-section">[default]</span>
<span class="hljs-attr">cache</span>=default-cache
<span class="hljs-attr">ttl</span>=<span class="hljs-number">600</span>
</code></pre>
<h2 id="heading-how-to-set-and-get-cache-data">How to Set and Get Cache Data</h2>
<p>To set cache data is straightforward. You have the <code>cache set</code> and the <code>cache get</code> subcommands:</p>
<pre><code class="lang-bash">momento cache <span class="hljs-built_in">set</span> --key <span class="hljs-string">"andrew"</span> --value <span class="hljs-string">"brown"</span> 
momento cache get --key <span class="hljs-string">"andrew"</span>
&gt; brown
</code></pre>
<h2 id="heading-how-to-create-a-new-cache">How to Create a New Cache</h2>
<p>We can create a another cache instantly with the <code>cache create</code> command. And we'll supply the <code>--name</code> flag to the <code>cache get</code> and <code>cache set</code>:</p>
<pre><code class="lang-bash">momento cache create --name freecodecamp
momento cache <span class="hljs-built_in">set</span> --name freecodecamp --key <span class="hljs-string">"Quincy"</span> --value <span class="hljs-string">"Larson"</span> 
momento cache get --name freecodecamp --key <span class="hljs-string">"Quincy"</span> 
&gt; Larson
</code></pre>
<h2 id="heading-how-to-integrate-momento-directly-into-your-web-application-code">How to Integrate Momento Directly into Your Web Application Code</h2>
<p>To use Momento within backend web-application code, we need to use one of the provided SDKs.</p>
<p>Let's write an example of using Momento in a Flask (Python) web-application using the Momento Python SDK.</p>
<p>Here is what our Flask app looks without using caching:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> psycopg2
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template
<span class="hljs-keyword">import</span> json

app = Flask(__name__)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db_connection</span>():</span>
    conn = psycopg2.connect(host=<span class="hljs-string">'localhost'</span>,
                            database=<span class="hljs-string">'flask_db'</span>,
                            user=os.environ[<span class="hljs-string">'DB_USERNAME'</span>],
                            password=os.environ[<span class="hljs-string">'DB_PASSWORD'</span>])
    <span class="hljs-keyword">return</span> conn

<span class="hljs-meta">@app.route('/')</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    json_data = get_free_courses()

    response = app.response_class(
        response=json_data,
        status=<span class="hljs-number">200</span>,
        mimetype=<span class="hljs-string">'application/json'</span>
    )
    <span class="hljs-keyword">return</span> response

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_free_courses</span>():</span>
  json_data = <span class="hljs-literal">None</span>
  conn = get_db_connection()
  cur = conn.cursor()

  cur.execute(<span class="hljs-string">'SELECT * FROM free_courses;'</span>)
  free_courses = cur.fetchall()

  json_data = json.dumps(free_courses)

  cur.close()
  conn.close()
  <span class="hljs-keyword">return</span> json_data
</code></pre>
<p>Here is what our application would look like implementing Momento:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> psycopg2
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> momento <span class="hljs-keyword">import</span> simple_cache_client <span class="hljs-keyword">as</span> scc

_MOMENTO_AUTH_TOKEN  = os.getenv(<span class="hljs-string">'MOMENTO_AUTH_TOKEN'</span>)
_MOMENTO_TTL_SECONDS = os.getenv(<span class="hljs-string">'MOMENTO_TTL_SECONDS'</span>)
_MOMENTO_CACHE_NAME  = os.getenv(<span class="hljs-string">'_MOMENTO_CACHE_NAME'</span>)

app = Flask(__name__)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db_connection</span>():</span>
    conn = psycopg2.connect(host=<span class="hljs-string">'localhost'</span>,
                            database=<span class="hljs-string">'flask_db'</span>,
                            user=os.environ[<span class="hljs-string">'DB_USERNAME'</span>],
                            password=os.environ[<span class="hljs-string">'DB_PASSWORD'</span>])
    <span class="hljs-keyword">return</span> conn

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
  <span class="hljs-keyword">with</span> scc.SimpleCacheClient(_MOMENTO_AUTH_TOKEN, _MOMENTO_TTL_SECONDS) <span class="hljs-keyword">as</span> cache_client:
    key = <span class="hljs-string">'get_free_courses'</span>
    get_resp = cache_client.get(_CACHE_NAME, <span class="hljs-string">'get_free_courses'</span>)
    <span class="hljs-keyword">if</span> get_resp.status() == <span class="hljs-string">'hit'</span>:
      json_data = get_resp.value()
    <span class="hljs-keyword">elif</span> get_resp.status() == <span class="hljs-string">'miss'</span>:
      json_data = get_free_courses()
      cache_client.set(_CACHE_NAME, <span class="hljs-string">'get_free_courses'</span>, json_data)

    response = app.response_class(
        response=json_data,
        status=<span class="hljs-number">200</span>,
        mimetype=<span class="hljs-string">'application/json'</span>
    )
    <span class="hljs-keyword">return</span> response

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_free_courses</span>():</span>
  json_data = <span class="hljs-literal">None</span>
  conn = get_db_connection()
  cur = conn.cursor()

  cur.execute(<span class="hljs-string">'SELECT * FROM free_courses;'</span>)
  free_courses = cur.fetchall()

  json_data = json.dumps(free_courses)

  cur.close()
  conn.close()
  <span class="hljs-keyword">return</span> json_data
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>If you want to give Momento a go, visit their website documentation for more information.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://docs.momentohq.com/">https://docs.momentohq.com/</a></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Redis Database Basics – How the Redis CLI Works, Common Commands, and Sample Projects ]]>
                </title>
                <description>
                    <![CDATA[ By Mehul Mohan Redis is a popular in-memory database used for a variety of projects, like caching and rate limiting. In this blog post, we will see how you can use Redis as an in-memory database, why you'd want to use Redis, and finally we'll discuss... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-learn-redis/</link>
                <guid isPermaLink="false">66d46059e39d8b5612bc0dd7</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 14 Apr 2021 15:55:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/04/image-46.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mehul Mohan</p>
<p>Redis is a popular in-memory database used for a variety of projects, like caching and rate limiting.</p>
<p>In this blog post, we will see how you can use Redis as an in-memory database, why you'd want to use Redis, and finally we'll discuss a few important features of the database. Let's start.</p>
<h2 id="heading-what-is-an-in-memory-database">What is an in-memory database?</h2>
<p>Traditional databases keep part of the database (usually the "hot" or often-accessed indices) in memory for faster access, and the rest of the database on disk. </p>
<p>Redis, on the other hand, focuses a lot on latency and the fast retrieval and storage of data. So it operates completely on memory (RAM) instead of storage devices (SSD/HDD). Speed is important!</p>
<p>Redis is a key-value database. But don't let it fool you into thinking it's a simple database. You have a lot of ways to store and retrieve those keys and values.</p>
<h2 id="heading-why-do-you-need-redis">Why do you need Redis?</h2>
<p>You can use Redis in a lot of ways. But there are two main reasons I can think of:</p>
<ol>
<li>You are creating an application where you want to make your code layer stateless. Why? - Because if your code is stateless, it is horizontally scalable. Therefore, you can use Redis as a central storage system and let your code handle just the logic.</li>
<li>You are creating an application where multiple apps might need to share data. For example, what if somebody is trying to bruteforce your site at <code>payments.codedamn.com</code>, and once you detect it, you'd also like to block them at <code>login.codedamn.com</code>? Redis lets your multiple disconnected/loosely connected services share a common memory space.</li>
</ol>
<h2 id="heading-redis-basics">Redis Basics</h2>
<p>Redis is relatively simple to learn as there are only a handful of commands you'll need to know. In the next couple sections, we'll cover a few main Redis concepts and some useful common commands.</p>
<h3 id="heading-the-redis-cli">The Redis CLI</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/ezgif.com-gif-maker.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Redis has a CLI which is a REPL version of the command line. Whatever you write will be evaluated. </p>
<p>The above image shows you how to do a simple <code>PING</code> or hello world in Redis in one of my codedamn Redis course exercises (the course is linked at the end if you want to check it out).</p>
<p>This Redis REPL is very useful when you're working with the database in an application and quickly need to get a peek into a few keys or the state of Redis.</p>
<h2 id="heading-common-redis-commands">Common Redis commands</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/ezgif.com-gif-maker--1-.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Trying out common commands on Redis CLI in codedamn course</em></p>
<p>Here are a few very commonly used commands in Redis to help you learn more about how it works:</p>
<h3 id="heading-set">SET</h3>
<p>SET allows you to set a key to a value in Redis.</p>
<p>Here's an example of how it works:</p>
<pre><code>SET mehul <span class="hljs-string">"developer from india"</span>
</code></pre><p>This sets the key <code>mehul</code> to the value <code>developer from india</code>.</p>
<h3 id="heading-get">GET</h3>
<p>GET allows you to get the keys you've set.</p>
<p>Here's the syntax:</p>
<pre><code>GET mehul
</code></pre><p>This will return the string "developer from india" as we set above.</p>
<h3 id="heading-setnx">SETNX</h3>
<p>This key will set a value only if the key does not exist. This command has a number of use cases, including not accidentally overwriting the value of a key which might already be present.</p>
<p>Here's how it works:</p>
<pre><code>SET key1 value1
SETNX key1 value2
SETNX key2 value2
</code></pre><p>After running this example, your <code>key1</code> will have the value <code>value1</code> and <code>key2</code> as <code>value2</code>. This is because the second command will have no effect as <code>key1</code> was already present.</p>
<h3 id="heading-mset">MSET</h3>
<p>MSET is like SET, but you can set multiple keys together in one command. Here's how it works:</p>
<pre><code>MSET key1 <span class="hljs-string">"value1"</span> key2 <span class="hljs-string">"value2"</span> key3 <span class="hljs-string">"value3"</span>
</code></pre><p>Right now we are using <code>key</code> and <code>value</code> as the prefix for keys and values. But in reality when you write such code it's easy to lose track of what is a key and what is a value in such a long command. </p>
<p>So one thing you can do is always quote your value using double quotes, and leave your keys without quotes (if they are valid keynames without quotes).</p>
<h3 id="heading-mget">MGET</h3>
<p>MGET is similar to GET, but it can return multiple values at once, like this:</p>
<pre><code>MGET key1 key2 key3 key4
</code></pre><p>This will return four values as an array: <code>value1</code>, <code>value2</code>, <code>value3</code> and <code>null</code>. We got <code>key4</code> as null because we never set it.</p>
<h3 id="heading-del">DEL</h3>
<p>This command deletes a key – simple enough, right?</p>
<p>Here's an example:</p>
<pre><code>SET key value
GET key # gives you <span class="hljs-string">"value"</span>
DEL key 
GET key # <span class="hljs-literal">null</span>
</code></pre><h3 id="heading-incr-and-decr">INCR and DECR</h3>
<p>You can use these two commands to increment or decrement a key which is a number. They are very useful and you'll use them a lot, because Redis can perform two operations in one – GET key and SET key to key + 1. </p>
<p>This avoids roundtrips to your parent application, and makes the operation also safe to perform without using transactions (more on this later)</p>
<p>Here's how they work:</p>
<pre><code>SET favNum <span class="hljs-number">10</span>
INCR favNum # <span class="hljs-number">11</span>
INCR favNum # <span class="hljs-number">12</span>
DECR favNum # <span class="hljs-number">11</span>
</code></pre><h3 id="heading-expire">EXPIRE</h3>
<p>The EXPIRE command is used to set an expiration timer to a key. Technically it's not a timer, but a kill timestamp beyond which the key will always return null unless it's set again.</p>
<pre><code>SET bitcoin <span class="hljs-number">100</span>
EXPIRE bitcoin <span class="hljs-number">10</span>

GET bitcoin # <span class="hljs-number">100</span>
# after <span class="hljs-number">10</span> seconds
GET bitcoin # <span class="hljs-literal">null</span>
</code></pre><p><code>EXPIRE</code> uses a little bit more memory to store that key as a whole (because now you have to also store when that key should expire). But you probably won't ever care about that overhead.</p>
<h3 id="heading-ttl">TTL</h3>
<p>This command can be used to learn how much time the key has to live.</p>
<p>Example:</p>
<pre><code>SET bitcoin <span class="hljs-number">100</span>
TTL bitcoin # <span class="hljs-number">-1</span>
TTL somethingelse # <span class="hljs-number">-2</span>

EXPIRE bitcoin <span class="hljs-number">5</span>
# wait <span class="hljs-number">2</span> seconds
TTL bitcoin # returns <span class="hljs-number">3</span>
# after <span class="hljs-number">1</span> second
GET bitcoin # <span class="hljs-literal">null</span>
TTL bitcoin # <span class="hljs-number">-2</span>
</code></pre><p>So what can we learn from this code?</p>
<ol>
<li>TTL will return <code>-1</code> if the key exists but doesn't have an expiration</li>
<li>TTL will return <code>-2</code> if the key doesn't exist</li>
<li>TTL will return time to live in seconds if the key exists and will expire</li>
</ol>
<h3 id="heading-setex">SETEX</h3>
<p>You can perform <strong>SET</strong> and <strong>EXPIRE</strong> together with <code>SETEX</code>.</p>
<p>Like this:</p>
<pre><code>SETEX key <span class="hljs-number">10</span> value
</code></pre><p>Here, the key is "key", the value is "value", and the time to live (TTL) is 10. This key will get unset after 10 seconds.</p>
<p>Now that you have fundamental knowledge of basic Redis commands and how the CLI works, let's build a couple of projects and use those tools in real life.</p>
<h2 id="heading-project-1-build-an-api-caching-system-with-redis">Project 1 – Build an API Caching System with Redis</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screenshot-2021-04-13-at-4.20.13-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Preview of API caching system building lab on codedamn</em></p>
<p>This project involves setting up an API caching system with Redis, where you cache results from a 3rd party server and use it for some time. </p>
<p>This is useful so that you are not rate limited by that third party. Also, caching improves your site's speed, so if you implement it correctly it's a win-win for everyone.</p>
<p>You can build this project interactively on codedamn inside the browser using Node.js. If you're interested, you can <a target="_blank" href="https://codedamn.com/learn/redis-caching-concepts-nodejs/2--CvP86ikcFeFUB4MTty">try the API caching lab for free</a>.</p>
<p>If you're only interested in the solution (and not building it yourself) here's how the core logic will work in Node.js:</p>
<pre><code class="lang-js">app.post(<span class="hljs-string">'/data'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">const</span> repo = req.body.repo

    <span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> redis.get(repo)

    <span class="hljs-keyword">if</span> (value) {
        <span class="hljs-comment">// means we got a cache hit</span>
        res.json({
            <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span>,
            <span class="hljs-attr">stars</span>: value
        })

        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://api.github.com/repos/<span class="hljs-subst">${repo}</span>`</span>).then(<span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> t.json())

    <span class="hljs-keyword">if</span> (response.stargazers_count != <span class="hljs-literal">undefined</span>) {
        <span class="hljs-keyword">await</span> redis.setex(repo, <span class="hljs-number">60</span>, response.stargazers_count)
    }

    res.json({
        <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span>,
        <span class="hljs-attr">stars</span>: response.stargazers_count
    })
})
</code></pre>
<p>Let's see what's happening here:</p>
<ul>
<li>We try to get the <code>repo</code> (which is the passed repo format - <code>facebook/react</code>) from our Redis cache. If present, great! We return the star count from our redis cache, saving us a roundtrip to GitHub's servers.</li>
<li>If we don't find it in cache, we do a request to GitHub's servers, and get the star count. We check if the star count is not undefined (in case a repo doesn't exist/is private). If it has a value, we <code>setex</code> the value with a timeout of 60 seconds.</li>
<li>We set a timeout because we don't want to serve stale values over time. This helps us refresh our star count at least once a minute.</li>
</ul>
<p>Here's the full source code:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/codedamn-classrooms/redis-nodejs-classroom/tree/lab5sol">https://github.com/codedamn-classrooms/redis-nodejs-classroom/tree/lab5sol</a></div>
<h2 id="heading-project-2-rate-limiting-api-with-redis">Project 2 - Rate limiting API with Redis</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screenshot-2021-04-13-at-4.21.31-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Preview of rate limiting API with Redis</em></p>
<p>This project involves rate limiting a certain endpoint to protect it from bad actors, and then blocking them from accessing that particular API. </p>
<p>This is very useful for login and sensitive API endpoints, where you don't want a single person to hit your endpoint with thousands of requests.</p>
<p>We perform rate limiting by IP address in this lab. If you want to attempt this codelab, you can try <a target="_blank" href="https://codedamn.com/learn/redis-caching-concepts-nodejs/RapJaltQbkvVtm4_b5oYl">it for free</a> on codedamn.</p>
<p>If you're only interested in the solution (and not building it yourself) here's how the core logic will work in Node.js:</p>
<pre><code>app.post(<span class="hljs-string">'/api/route'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-comment">// add data here</span>
    <span class="hljs-keyword">const</span> ip = req.headers[<span class="hljs-string">'x-forwarded-for'</span>] || req.ip

    <span class="hljs-keyword">const</span> reqs = <span class="hljs-keyword">await</span> redis.incr(ip)
    <span class="hljs-keyword">await</span> redis.expire(ip, <span class="hljs-number">2</span>)

    <span class="hljs-keyword">if</span> (reqs &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">return</span> res.json({
            <span class="hljs-attr">status</span>: <span class="hljs-string">'rate-limited'</span>
        })
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (reqs &gt; <span class="hljs-number">10</span>) {
        <span class="hljs-keyword">return</span> res.json({
            <span class="hljs-attr">status</span>: <span class="hljs-string">'about-to-rate-limit'</span>
        })
    } <span class="hljs-keyword">else</span> {
        res.json({
            <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span>
        })
    }
})
</code></pre><p>Let's understand this code block:</p>
<ul>
<li>We try to extract the IP from the <code>x-forwarded-for</code> header (or you can use <code>req.ip</code> as we are using express)</li>
<li>We <code>INCR</code> the IP address field. If our key in Redis never existed, INCR would automatically set it to 0 and increment, that is finally set it to 1.</li>
<li>We set the key to expire in 2 seconds. Ideally you'd want a larger value - but this is what the codedamn challenge specified above, so there we have it.</li>
<li>Finally we check the request counts, if they are greater than a certain threshold, we block the request from reaching the main function body.</li>
</ul>
<p>Here's the full solution:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/codedamn-classrooms/redis-nodejs-classroom/tree/lab6sol">https://github.com/codedamn-classrooms/redis-nodejs-classroom/tree/lab6sol</a></div>
<h2 id="heading-more-on-redis">More on Redis</h2>
<p>Redis is much more than what we have learned so far. But the good thing is that we have learned enough to start working with it already! </p>
<p>In this section, let's cover a few more Redis fundamentals.</p>
<h3 id="heading-redis-is-single-threaded">Redis is single threaded</h3>
<p>Redis runs as a single threaded process, even on a multiple core system supporting multi threading. This is not a performance nightmare, but a safety measure against inconsistent read/writes in a multi threaded environment. </p>
<p>If Redis were multi threaded, to ensure thread safety when accessing a single key, you'd eventually have resolved to some locking mechanism, which probably would perform worse than single threaded/sequential access anyway.</p>
<h3 id="heading-redis-transactions">Redis Transactions</h3>
<p>Of course, you cannot do everything in Redis in a single command. But you can surely ask it to do a block of commands in a single go (that is, nobody else talks to Redis while it is executing that block). You can do that using the <code>MULTI</code> command.</p>
<p>Here's how that works:</p>
<pre><code>MULTI
SET hello world
SET yo lo
SET number <span class="hljs-number">1</span>
INCR number
EXPIRE hello <span class="hljs-number">10</span>
EXPIRE yo <span class="hljs-number">5</span>
EXEC
</code></pre><p>This will perform all these operations in one go, that is it will <strong>not</strong> run anything at all after <code>MULTI</code>, and will run everything at once the moment it sees the <code>EXEC</code> keyword.</p>
<p>Redis includes support for lists and sets for more advanced use cases. You can also use Redis as a broadcasting service where you <strong>publish</strong> to a channel and others who have <strong>subscribed</strong> to the channel receive a notification. This is very useful in multi-client architecture.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you liked this introduction to Redis. This blog post is a part of codedamn's <a target="_blank" href="https://codedamn.com/learn/redis-caching-concepts-nodejs">new interactive course: Redis + Node.js caching</a>, where you not only learn about these concepts, but practice them within your browser on the go. </p>
<p>Feel free to give the course a try and let me know what you think. You can find me on <a target="_blank" href="https://twitter.com/mehulmpt">twitter</a> to send any feedback :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Setup Instagram-like Video Stories in Your App ]]>
                </title>
                <description>
                    <![CDATA[ By Agam Mahajan The article will teach you how you can show multiple videos in one view, like we see in Instagram Stories. We'll also learn how to cache the videos in the user's device to help save that user's data and network calls and smooth out th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/video-stories-and-caching-ios/</link>
                <guid isPermaLink="false">66d45d5cbd438296f45cd377</guid>
                
                    <category>
                        <![CDATA[ app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ios app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ video ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 22 Sep 2020 22:01:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/1_gYkQNP0BaohLJ8hDKL1C6w-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Agam Mahajan</p>
<p>The article will teach you how you can show multiple videos in one view, like we see in Instagram Stories.</p>
<p>We'll also learn how to cache the videos in the user's device to help save that user's data and network calls and smooth out their experience.</p>
<p>A quick note: this implementation is for iOS, but the same logic can be applied in other codebases as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/ezgif.com-video-to-gif--5-.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In general, whenever we want to play a video, we get the video URL and simply present <code>**AVPlayerViewController**</code> with that URL.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> videoURL = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"Sample-Video-Url"</span>)
<span class="hljs-keyword">let</span> player = <span class="hljs-type">AVPlayer</span>(url: videoURL!)
<span class="hljs-keyword">let</span> playerViewController = <span class="hljs-type">AVPlayerViewController</span>()
playerViewController.player = player
<span class="hljs-keyword">self</span>.present(playerViewController, animated: <span class="hljs-literal">true</span>) {
    playerViewController.player.play()
}
</code></pre>
<p>Pretty straightforward, right?</p>
<p>But the drawback of this implementation is that you <strong>can’t</strong> <strong>customiz</strong>e it. Which, if you are working for a good product company, will be an everyday ask. :D</p>
<p>Alternatively, we can use <code>**AVPlayerLayer**</code> which will do a similar job – but it allows us to customize the view and other elements.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> videoURL = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"Sample-Video-Url"</span>)
<span class="hljs-keyword">let</span> player = <span class="hljs-type">AVPlayer</span>(url: videoURL!)
<span class="hljs-keyword">let</span> playerLayer = <span class="hljs-type">AVPlayerLayer</span>(player: player)
playerLayer.frame = <span class="hljs-keyword">self</span>.view.bounds
<span class="hljs-keyword">self</span>.view.layer.addSublayer(playerLayer)
player.play()
</code></pre>
<p>But what if you want to combine multiple videos, similar to <strong>Instagram stories</strong>? Then we probably have to dive in a bit deeper.</p>
<h2 id="heading-coming-back-to-the-problem-statement">Coming Back to the Problem Statement</h2>
<p>Now, let me tell you about my use case.</p>
<p>In my company, Swiggy, we want to be able to show multiple videos, where each video should be shown x number of times.</p>
<p>On top of that, it should have an Instagram-like stories feature.</p>
<ul>
<li>Video-2 should seamlessly autoplay after video-1, and so on</li>
<li>It should jump to corresponding videos whenever the user taps left or right.</li>
</ul>
<p>If you think caching could be the answer, don't worry – I’ll get to that in a bit.</p>
<h3 id="heading-multiple-layers-in-one-view">Multiple layers in one view</h3>
<p>First things first, we need to figure out how to add multiple videos in one view.</p>
<p>What we can do is create one <code>**AVPlayerLayer**</code> and assign the first video to it. When the first video is finished, then we assign the next video to the same <code>**AVPlayerLayer**</code> .</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">addPlayer</span><span class="hljs-params">(player: AVPlayer)</span></span> {
    player.currentItem?.seek(to: <span class="hljs-type">CMTime</span>.zero, completionHandler: <span class="hljs-literal">nil</span>)
    playerViewModel?.player = player
    playerView.playerLayer.player = player
}
</code></pre>
<p>To jump to the previous or next video, we can do the following:</p>
<ul>
<li>Add a tap gesture on the view</li>
<li>If the touch location ‘x’ is less than half of the screen, then assign the previous video, else assign the next video</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-meta">@objc</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">didTapSnap</span><span class="hljs-params">(<span class="hljs-number">_</span> sender: UITapGestureRecognizer)</span></span> {
   <span class="hljs-keyword">let</span> touchLocation = sender.location(ofTouch: <span class="hljs-number">0</span>, <span class="hljs-keyword">in</span>: view)
   <span class="hljs-keyword">if</span> touchLocation.x &lt; view.frame.width/<span class="hljs-number">2</span> {
     changePlayer(forward: <span class="hljs-literal">false</span>)
     } 
   <span class="hljs-keyword">else</span> {
     fillupLastPlayedSnap()
     changePlayer(forward: <span class="hljs-literal">true</span>)
    }
}
</code></pre>
<p>There we go. We now have our own Insta-like Stories video feature.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/13ZwNq4FnbM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>But our task is not done yet!</p>
<h2 id="heading-now-back-to-caching">Now Back to Caching</h2>
<p>We don't want it to be the case that every time a user navigates from one video to another, it starts to download the video from the beginning.</p>
<p>Also, if the video is shown again in the next session, we don't need to do another server call. </p>
<p>If we can cache the video, then the user’s internet will be saved. The load on the server will also be reduced.</p>
<p>Finally, the UX will improve as the user won't have to wait a long time to load the video.</p>
<p><strong>As a good developer, reducing</strong> a <strong>user’s internet usage should be our priority.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/less-data-usage-happy-customer.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Less data usage, happy customer</em></p>
<h3 id="heading-load-videos-asynchronously">Load Videos <strong>Asynchronously</strong></h3>
<p>The first thing we can use to load videos is <strong>loadValuesAsynchronously</strong>.</p>
<p>According to <a target="_blank" href="https://developer.apple.com/documentation/avfoundation/avasynchronouskeyvalueloading/1387321-loadvaluesasynchronously">the Apple documentation</a>, <strong>loadValuesAsynchronously:</strong></p>
<blockquote>
<p><em>Tells the asset to load the values of all of the specified keys (property names) that are not already loaded.</em></p>
</blockquote>
<p>The advantage here is that it saves the video until it is rendered. So it will not download the video from the start whenever the user navigates to a previous video. It will only download the part which was not rendered earlier.</p>
<p><strong>Let's look at an e</strong>xample<em>**</em>: say we have Video_1 that is 15 seconds long, and the user saw 10 seconds of that video before jumping to Video_2. </p>
<p>Now if the user comes back to Video_1 again by tapping to the left, <strong>loadValuesAsynchronously</strong> will have that 10 seconds of video saved and will only download the remaining (unwatched) 5 seconds.</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">asynchronouslyLoadURLAssets</span><span class="hljs-params">(<span class="hljs-number">_</span> newAsset: AVURLAsset)</span></span> {
    <span class="hljs-type">DispatchQueue</span>.main.async {
            newAsset.loadValuesAsynchronously(forKeys: <span class="hljs-keyword">self</span>.assetKeysRequiredToPlay) {
                <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.assetKeysRequiredToPlay {
                    <span class="hljs-keyword">var</span> error: <span class="hljs-type">NSError?</span>
                    <span class="hljs-keyword">if</span> newAsset.statusOfValue(forKey: key, error: &amp;error) == .failed {
                        <span class="hljs-keyword">self</span>.delegate?.playerDidFailToPlay(message: <span class="hljs-string">"Can't use this AVAsset because one of it's keys failed to load"</span>)
                        <span class="hljs-keyword">return</span>
                    }
                }

                <span class="hljs-keyword">if</span> !newAsset.isPlayable || newAsset.hasProtectedContent {
                    <span class="hljs-keyword">self</span>.delegate?.playerDidFailToPlay(message: <span class="hljs-string">"Can't use this AVAsset because it isn't playable or has protected content"</span>)
                    <span class="hljs-keyword">return</span>
                }
                <span class="hljs-keyword">let</span> currentItem = <span class="hljs-type">AVPlayerItem</span>(asset: newAsset)
                <span class="hljs-keyword">let</span> currentPlayer = <span class="hljs-type">AVPlayer</span>(playerItem: currentItem)
                <span class="hljs-keyword">self</span>.delegate?.playerDidSuccesToPlay(playerDetail: currentPlayer)
            }

        }
</code></pre>
<p>You can find more details on <strong>loadValuesAsynchronously</strong> at this <a target="_blank" href="https://developer.apple.com/documentation/avfoundation/avasynchronouskeyvalueloading/1387321-loadvaluesasynchronously">link</a>.</p>
<p>The caveat here is it persists video data for that session only. If the user closes and comes back to the app, the video has to be downloaded again.</p>
<p>So what other options do we have?</p>
<h3 id="heading-saving-videos-in-device">Saving Videos in Device</h3>
<p>Now comes <strong>Video Caching</strong>!</p>
<p>When the video is rendered completely, we can export the video and save it to the user’s device. When the video comes up again in their next session, we can pick the video from the device and simply load it.</p>
<p><strong>AVAssetExportSession</strong><br>According to <a target="_blank" href="https://developer.apple.com/documentation/avfoundation/avassetexportsession">Apple's documentation</a>:</p>
<blockquote>
<p><em>An object that transcodes the contents of an asset source object to create an output of the form described by a specified export preset.</em></p>
</blockquote>
<p>This means that AVAssetExportSession acts as an exporter, through which we can save the file to the user’s device. We have to give the output URL and the output file type.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> exporter = <span class="hljs-type">AVAssetExportSession</span>(asset: avUrlAsset, presetName: <span class="hljs-type">AVAssetExportPresetHighestQuality</span>)
exporter?.outputURL = outputURL
exporter?.outputFileType = <span class="hljs-type">AVFileType</span>.mp4

exporter?.exportAsynchronously(completionHandler: {
    <span class="hljs-built_in">print</span>(exporter?.status.rawValue)
    <span class="hljs-built_in">print</span>(exporter?.error)
})
</code></pre>
<p>You can find more details on <strong>AVAssetExportSession</strong> at this <a target="_blank" href="https://developer.apple.com/documentation/avfoundation/avassetexportsession">link</a>.</p>
<p>Now the only thing left is to fetch the data from the cache and load the video.</p>
<p>Before loading, check if the video is present in the cache. Then fetch that local URL and give it to <strong>loadValuesAsynchronously.</strong></p>
<pre><code class="lang-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> cacheUrl = <span class="hljs-type">FindCachedVideoURL</span>(forVideoId: videoId) {
    <span class="hljs-keyword">let</span> cacheAsset = <span class="hljs-type">AVURLAsset</span>(url: cacheUrl)
    asynchronouslyLoadURLAssets(cacheAsset)
}
<span class="hljs-keyword">else</span> {
  asynchronouslyLoadURLAssets(newAsset)
}
</code></pre>
<p>Caching will help reduce a lot of user data usage as well as server load (sometimes up to TBs of data).</p>
<h2 id="heading-other-use-cases-for-caching">Other use cases for caching</h2>
<p>What other use cases we can handle with caching? The following are examples of ways you could use caching here:</p>
<h3 id="heading-ensure-optimum-storage">Ensure Optimum Storage</h3>
<p>Before saving the video on the device, you should check whether enough storage is present on the device to do so.</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isStorageAvailable</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Bool</span> {
   <span class="hljs-keyword">let</span> fileURL = <span class="hljs-type">URL</span>(fileURLWithPath: <span class="hljs-type">NSHomeDirectory</span>() <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>)
   <span class="hljs-keyword">do</span> {
      <span class="hljs-keyword">let</span> values = <span class="hljs-keyword">try</span> fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey, .volumeTotalCapacityKey])
      <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> totalSpace = values.volumeTotalCapacity,
      <span class="hljs-keyword">let</span> freeSpace = values.volumeAvailableCapacityForImportantUsage <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
      }
      <span class="hljs-keyword">if</span> freeSpace &gt; minimumSpaceRequired {
         <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
      } <span class="hljs-keyword">else</span> {
          <span class="hljs-comment">// Capacity is unavailable</span>
          <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
      }  
    <span class="hljs-keyword">catch</span> {}
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
}
</code></pre>
<h3 id="heading-remove-deprecated-videos">Remove Deprecated Videos</h3>
<p>You can have a timestamp for each video so that you can clean up old videos from device memory after a certain number of days.</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cleanExpiredVideos</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">let</span> currentTimeStamp = <span class="hljs-type">Date</span>().timeIntervalSince1970
        <span class="hljs-keyword">var</span> expiredKeys: [<span class="hljs-type">String</span>] = []
        <span class="hljs-keyword">for</span> videoData <span class="hljs-keyword">in</span> videosDict <span class="hljs-keyword">where</span> currentTimeStamp - videoData.value.timeStamp &gt;= expiryTime {
            <span class="hljs-comment">// video is expired. delete</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-number">_</span> = popupVideosDict[videoData.key] {
                expiredKeys.append(videoData.key)
            }
        }
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> expiredKeys {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-number">_</span> = popupVideosDict[key] {
                popupVideosDict.removeValue(forKey: key)
                deleteVideo(<span class="hljs-type">ForVideoId</span>: key)
            }
        }
    }
</code></pre>
<h3 id="heading-maintain-a-limited-number-of-videos">Maintain a limited number of videos</h3>
<p>You can make sure only a limited number of videos are saved in the file at a time. Let's say 10. </p>
<p>Then when the 11th video comes, you can have it delete the least-viewed video and replace it with the new one. This will also help you not consume too much of the user’s device memory.</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">removeVideoIfMaxNumberOfVideosReached</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> popupVideosDict.<span class="hljs-built_in">count</span> &gt;= maxVideosAllowed {
            <span class="hljs-comment">// remove the least recently used video</span>
            <span class="hljs-keyword">let</span> sortedDict = popupVideosDict.keysSortedByValue { (v1, v2) -&gt; <span class="hljs-type">Bool</span> <span class="hljs-keyword">in</span>
                v1.timeStamp &lt; v2.timeStamp
            }
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> videoId = sortedDict.first <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span>
            }
            popupVideosDict.removeValue(forKey: videoId)
            deleteVideo(<span class="hljs-type">ForVideoId</span>: videoId)
        }
    }
</code></pre>
<h3 id="heading-measure-impact">Measure Impact</h3>
<p>Don’t forget to add logs, so that you can measure the impact of your feature. I have used a custom New Relic Log Event to do so:</p>
<pre><code class="lang-swift"> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findCachedVideoURL</span><span class="hljs-params">(forVideoId id: String)</span></span> -&gt; <span class="hljs-type">URL?</span> {
        <span class="hljs-keyword">let</span> nsDocumentDirectory = <span class="hljs-type">FileManager</span>.<span class="hljs-type">SearchPathDirectory</span>.documentDirectory
        <span class="hljs-keyword">let</span> nsUserDomainMask = <span class="hljs-type">FileManager</span>.<span class="hljs-type">SearchPathDomainMask</span>.userDomainMask
        <span class="hljs-keyword">let</span> paths = <span class="hljs-type">NSSearchPathForDirectoriesInDomains</span>(nsDocumentDirectory, nsUserDomainMask, <span class="hljs-literal">true</span>)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> dirPath = paths.first {
            <span class="hljs-keyword">let</span> fileURL = <span class="hljs-type">URL</span>(fileURLWithPath: dirPath).appendingPathComponent(folderPath).appendingPathComponent(id + <span class="hljs-string">".mp4"</span>)
            <span class="hljs-keyword">let</span> filePath = fileURL.path
            <span class="hljs-keyword">let</span> fileManager = <span class="hljs-type">FileManager</span>.<span class="hljs-keyword">default</span>
            <span class="hljs-keyword">if</span> fileManager.fileExists(atPath: filePath) {
                <span class="hljs-type">NewRelicService</span>.sendCustomEvent(with: <span class="hljs-type">NewRelicEventType</span>.statusCodes,
                                                                   eventName: <span class="hljs-type">NewRelicEventName</span>.videoCacheHit,
                                                                   attributes: [<span class="hljs-type">NewRelicAttributeKey</span>.videoSize: fileURL.fileSizeString])
                <span class="hljs-keyword">return</span> fileURL
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
            }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }
</code></pre>
<p>To convert the file size to a readable format, I fetch the file size and convert it to Mbs.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">URL</span> </span>{
    <span class="hljs-keyword">var</span> attributes: [<span class="hljs-type">FileAttributeKey</span> : <span class="hljs-type">Any</span>]? {
        <span class="hljs-keyword">do</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">FileManager</span>.<span class="hljs-keyword">default</span>.attributesOfItem(atPath: path)
        } <span class="hljs-keyword">catch</span> <span class="hljs-keyword">let</span> error <span class="hljs-keyword">as</span> <span class="hljs-type">NSError</span> {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"FileAttribute error: \(error)"</span>)
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">var</span> fileSize: <span class="hljs-type">UInt64</span> {
        <span class="hljs-keyword">return</span> attributes?[.size] <span class="hljs-keyword">as</span>? <span class="hljs-type">UInt64</span> ?? <span class="hljs-type">UInt64</span>(<span class="hljs-number">0</span>)
    }

    <span class="hljs-keyword">var</span> fileSizeString: <span class="hljs-type">String</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">ByteCountFormatter</span>.string(fromByteCount: <span class="hljs-type">Int64</span>(fileSize), countStyle: .file)
    }
}
</code></pre>
<p>This is how you can measure your impact:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/Screenshot-2020-09-16-at-11.34.24-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Total data saved = n</strong>umber <strong>of request</strong>s <strong><em> video_size = 2.4MB</em>20.3K ~= 49GB</strong></p>
<p>This is just two weeks of data. You do the math for the whole year. ? And this will keep on increasing exponentially over time.</p>
<p>That’s it! You have now built your own caching mechanism.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/yay.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-wrapping-up">Wrapping up</h1>
<p>In this article, we saw how easily we can integrate multiple videos in one view, giving an Instagram-like story feature.</p>
<p>We also learned why and how caching plays an important role here. We saw how it helps the user save a lot of data and have a smooth user experience.</p>
<p>Do let me know if I missed something, or if you can think of any more use cases.<br>Thanks for your time. :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An In-depth Introduction to HTTP Caching: Cache-Control & Vary ]]>
                </title>
                <description>
                    <![CDATA[ Introduction - scope of the article This series of articles deals with caching in the context of HTTP. When properly done, caching can increase the performance of your application by an order of magnitude. On the contrary, when overlooked or complete... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/an-in-depth-introduction-to-http-caching-cache-control-and-vary/</link>
                <guid isPermaLink="false">66d460c5052ad259f07e4b2a</guid>
                
                    <category>
                        <![CDATA[ cache-http ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cache ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cache-control ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ http ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Léo Jacquemin ]]>
                </dc:creator>
                <pubDate>Thu, 24 Oct 2019 09:56:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/martin-adams-uZZw2vh8eqY-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h3 id="heading-introduction-scope-of-the-article">Introduction - scope of the article</h3>
<p>This series of articles deals with caching in the context of HTTP. When properly done, caching can increase the performance of your application by an order of magnitude. On the contrary, when overlooked or completely ignored, it can lead to some very unwanted side effects caused by misbehaving proxy servers that, in the absence of clear caching instructions, decide to cache anyway and serve stale resources.</p>
<p>In the first part of this series, we argued that caching is the most effective way to increase performance, when measured by the page load time. In this second part, it is time to shift our focus to the mechanisms at our disposal. To put it in another way: how does HTTP caching actually work?</p>
<p>To answer this question, we decided to consider the case of an empty cache that starts progressively caching and serving resources. As it gradually receives incoming HTTP requests, our cache will start behaving accordingly. Serving the resource from the cache when a fresh copy is available, varying over multiple representations, making a conditional request... This way, we can introduce each concept progressively as we need it.</p>
<p>At first, our empty cache will have no choice but to forward requests to the origin server. This will allow us to understand how origin servers instruct our cache on what to do with the resource, such as if it is allowed to store it, and for how long. For this, we will examine each Cache-Control directive and clarify some of them that have been known to have <a target="_blank" href="https://www.google.com/search?q=must-revalidate+vs+no-cache&amp;oq=must+revalidate+vs+&amp;aqs=chrome.1.69i57j0l3.3140j0j4&amp;sourceid=chrome&amp;ie=UTF-8">conflicting meanings</a>.</p>
<p>Second, we will look at what happens when our cache receives a request for a resource it already knows. How does our cache decide if it can re-use a previously stored response? How does it map a given HTTP request to a particular resource? To answer these, we will learn about representation variations with the Vary header.</p>
<p>This article is going to focus on knowledge that’s the most valuable from a web developer’s perspective. Therefore, conditional requests are only discussed briefly and will be the focus of another article.</p>
<p>Without further ado, let us start with an overview of what we will be exploring.</p>
<h2 id="heading-the-http-caching-decision-tree">The HTTP caching decision tree</h2>
<p>Conceptually, a cache system always involve at least three participants. With HTTP, these participants are the client, the server, and the caching proxy.</p>
<p>However, when learning about HTTP caching, we strongly encourage you not to think of the client as your typical web browser because these days, they all ship with their own HTTP caching layer. It makes it difficult to clearly separate the browser from the cache. For this reason, we invite you to think of the client as a headless command line program such as cURL or any application without an embedded HTTP cache.</p>
<p>All precautions aside, let us now deep dive into the subject by taking a look at the following picture: the HTTP caching decision tree.</p>
<p><img src="https://lh5.googleusercontent.com/4wnpOGgUnR2bJxcKqsUruzDIQrdmd5o956v85GGUARZKQYG77olAVBslIc_ZL1d0FZLVwlCuLLFeUzlSBYKaE-ALN-dWjijBbkzoVDuoTVQvG_GEAGABdZDXfl8TvBw2NdAgsnxk" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This picture illustrates all the possible paths a request can take every time a client asks for a resource to an origin server behind a caching system. A careful examination of this illustration reveals that there are only four possible outcomes.</p>
<p>Clearly separating these outcomes in our minds is actually very convenient, seeing as each important caching concept (cache instructions, representation matching, conditional requests and resource aging) maps to each one of them.</p>
<p>Let us describe succinctly each one by introducing two important terms relating to the HTTP caching terminology: cache hits and cache misses.</p>
<h3 id="heading-hits-and-misses">Hits and misses</h3>
<p>The first possible outcome is when the cache finds a matching resource, and is allowed to serve it, which, in the caching world, are indeed two distinct things. This outcome is what we commonly call a cache hit, and is the reason why we use caches in the first place.</p>
<p>When a cache hit happens, it completely offloads the origin server and the latency is dramatically reduced. In fact, when the cache hit happens in the browser’s HTTP cache latency is null and the requested resource is instantly available.</p>
<p>Unfortunately, cache hits account only one of the four possible outcomes. The rest of them fall into the second category, also known as cache misses, which can happen for only three reasons.</p>
<p>The first reason a cache miss typically happens is simply when the cache does not find any matching resource in its storage. This is usually a sign that the resource has never been requested before, or has been evicted from the cache to free up some space. In such cases, the proxy has no choice but to forward the request to the origin server, fully download the response and look for caching instructions in the response headers.</p>
<p>The second reason a cache miss can happen is actually just as detrimental, where the cache detects a matching representation, one that it could potentially use. However, the resource is not considered to be <em>fresh</em> anymore - we will see how exactly in the cache-control section of this article - but is said to be <em>stale.</em></p>
<p>In such case, the cache sends a special kind of request, called a <em>conditional request</em> to the origin server. Conditional requests allow caches to retrieve resources only if they are different from the one they have in their local storage. Since only the origin server ever has the most recent representation of a given resource, conditional requests <em>always</em> have to go through the whole caching proxy chain up to the origin server.</p>
<p>These special requests have only two possible outcomes. If the resource has not changed, the cache is instructed to use its local copy by receiving a 304 Not Modified response along with updated headers and an empty body. This outcome, the third one on our list, is called a successful validation.</p>
<p>Finally, the last possible outcome is when the resource has changed. In this case, the origin server sends a normal 200 OK response, as it would if the cache was empty and had forwarded the request. To put it another way, cache misses caused by empty cache and failed validation yield exactly the same HTTP response.</p>
<p>To best visualize these four paths, it is helpful to picture them in a timeline, as illustrated below.</p>
<p><img src="https://lh6.googleusercontent.com/eV4YKvBdmE_SD0dSlu7Gt4oQKW9IpekfHv5R_odd4m4Hq4HO71cgGez9MtxtGBd5ghP36tfWoj8OTMzE-N0iWiMI5WgOusUl7dOXUtLQM7MvywGqSaYuBRbS4oH-rbXdpdwTWW6h" alt="Image" width="600" height="400" loading="lazy"></p>
<p>At first, the cache is empty. The flow of requests starts with a cache miss (empty cache outcome). On its way back, the cache would read caching instructions and store the response. All subsequent requests for this particular resource would yield to cache hits, until the resource becomes stale and needs to be revalidated.</p>
<p>Upon a first revalidation, it is possible that the resource has not changed, hence, a 304 Not Modified would be sent.</p>
<p>Then, the resource eventually gets updated by a client, typically with a PUT or a PATCH request. When the next conditional request arrives, the origin server detects that the resource has changed and replies a 200 OK with updated ETag and Last-Modified headers.</p>
<p>Knowing about cache hits and cache misses along with the 4 possible paths that every cacheable request could take, should give you a good overview of how caching works.</p>
<p>Though overviews can only get you so far. In the following section, we will give a detailed explanation of how origin servers communicate caching instructions.</p>
<h3 id="heading-how-origin-servers-communicate-caching-instructions">How origin servers communicate caching instructions</h3>
<p>Origin servers communicate their caching instructions to downstream caching proxies by adding a Cache-Control header to their response. This header is an HTTP/1.1 addition and replaces the deprecated Pragma header, that was never a standard one.</p>
<p>Cache-control header values are called directives. The specification defines a lot of them, with various uses and <a target="_blank" href="https://www.mnot.net/blog/2017/03/16/browser-caching">browser-support</a>. These directives are primarily used by developers to communicate caching instructions. However, when present in an HTTP request, clients can also influence the caching decision. Let us now take the time to describe the most useful directives.</p>
<h3 id="heading-max-age">max-age</h3>
<p>The first important Cache-Control directive to know about is the max-age directive, which allows a server to specify the lifetime of a representation. It is expressed in seconds. For instance, if a cache sees a response containing the header Cache-Control: max-age=3600, it is allowed to store and serve the same response for all subsequent requests for this resource for the next 3600 seconds.</p>
<p>During these 3600 seconds, the resource will be considered fresh and cache hits will occur. Past this delay, the resource will become stale and validation will take over.</p>
<h3 id="heading-no-store-no-cache-must-revalidate">no-store, no-cache, must-revalidate</h3>
<p>Unlike max-age, the no-store, no-cache and must-revalidate directives are about instructing caches to not cache a resource. However, they differ in subtle ways.</p>
<p>no-store is pretty self-explanatory, and in fact, it does even a little more than the name suggests. When present, a HTTP/1.1 compliant cache must not attempt to store anything, and must also take actions to delete any copy it might have, either in memory, or stored on disk.</p>
<p>The no-cache directive, on the other hand, is arguably much less self-explanatory. This directive actually means to never use a local copy without first validating with the origin server. By doing so, it prevents all possibility of a cache hit, even with fresh resources.</p>
<p>To put it another way, the no-cache directive says that caches must revalidate their representations with the origin server. But then comes another directive, awkwardly named… must-revalidate.</p>
<p>If this starts to get confusing for you, rest assured, you are not alone. If what one wants is not to cache, it has to use no-store instead of no-cache. And if what one wants is to always revalidate, it has to use no-cache instead of must-revalidate.</p>
<p>Confusing, indeed.</p>
<p>As for the must-revalidate directive, it is used to forbid a cache to serve a stale resource. If a resource is fresh, must-revalidate perfectly allows a cache to serve it without forcing any revalidation, unlike with no-store and no-cache. That’s why this header should always be used with a max-age directive, to indicate a desire to cache a resource for some time and when it’s become stale, enforce a revalidation.</p>
<p>When it comes to these last three directives, we find the choice of words to describe each of them particularly confusing: no-store and no-cache are expressed negatively whereas must-revalidate is expressed positively. Their differences would probably be more obvious if they were to be expressed in the same fashion.</p>
<p>Therefore, it is helpful to think about each of them expressed in terms of what is not allowed:</p>
<ul>
<li><p><strong>no-store:</strong> never store anything</p>
</li>
<li><p><strong>no-cache:</strong> never cache hit</p>
</li>
<li><p><strong>must-revalidate:</strong> never serve stale</p>
</li>
</ul>
<p>Technically, these directives can appear in the same Cache-Control header. It is not uncommon to see them combined as a comma-separated list of values. A lot of popular websites still seem to behave very conservatively, sending back HTML pages with the following header:</p>
<p>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</p>
<p>When you stumble upon this, the intention behind it is usually pretty clear: the web development team wants to ensure that the resource never gets served stale to anyone.</p>
<p>However, such cache-buster lines are probably not necessary anymore. <a target="_blank" href="https://github.com/web-platform-tests/wpt/pull/5137">Past work</a> done in 2017 already showed that browsers are really rather compliant with the specification in respect to Cache-Control response directives. Therefore, unless you’re planning on setting up a caching stack with decades old software, you should be fine using just the directives you need. The most popular combinations will be analyzed in another article.</p>
<h3 id="heading-public-private">public, private</h3>
<p>The last important directives we haven’t discussed yet are a little bit different, as they control which types of caches are allowed to cache the resources. These are the public and private directives, private being the default one if unspecified.</p>
<p><img src="https://lh3.googleusercontent.com/kiq8Sq0igyLzRFkX4qddKF4y6xdltA1rXwjBOaqvWlqD1mJbaQe2WuLIparaOSfQ36iUT4kaHSKxzBY4TVbaVXtq7w3W6Hhq7QllsTf6WD2rAFq9MRG2AFNMI-EmUNmLn1TfmKnC" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Private caches are the ones that are supposed to be used by a single user. Typically, this is the web browser’s cache. CDN and reverse-proxies on the contrary, handle requests coming from multiple users.</p>
<p>Why do we need to distinguish these two types of caches ? The answer is straightforward: security, as illustrated by the following example.</p>
<p>Many web applications expose convenience endpoints that rely on information coming from elsewhere than the URL. If two users access their profile by requesting /users/me, at <a target="_blank" href="https://api.example/com/">https://api.example/com</a>, and their actual user id is hidden within a Authorization: Bearer 4Ja23ç42…. token, the cache won’t be able to tell these are in fact two very different resources.</p>
<p><img src="https://lh3.googleusercontent.com/48yzQ_RyKvQoWxgPmvvwijI74hSD_NNfjViTUDHeNvkmd-U-2wCqgCZWnmjRyTYNqwRGJPZJ-GuIoFbflCT_x6CCB6wIJGdHluEBK9BahnkL7pdzEmV9kwinkwJibC5JTLKAAGct" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Indeed, when constructing their cache key, caches do not inspect HTTP headers unless specifically instructed to do so, as we shall see in the next section.</p>
<h3 id="heading-s-maxage">s-maxage</h3>
<p>The s-maxage directive is like the max-age directive, except that it only applies to public caches, which are also referred to as <em>shared</em> caches (hence the s- prefix). If both directives are present, s-maxage will take precedence over max-age on public caches and be ignored on private ones.</p>
<p>When using this directive, the general rule is to always ensure that s-maxage value is below max-age’s. The reasoning behind this rule is that the closer you are to the origin, the more suitable it is to check frequently what the latest representation is.</p>
<p>Imagine you were to cache for one day in the proxy, and one hour in browsers.</p>
<p>Every time a browser would ask a resource to upstream servers, we could know <em>in</em> <em>advance</em> that the proxy will not contact the origin server for at least a day. Therefore, why not put the same TTL directly in the browsers ? As a conclusion, it is a best practice to always leave out a longer TTL in max-age than in s-maxage.</p>
<p><strong>stale-while-revalidate and stale-if-error</strong><br>These two directives are not technically part of the original specification but are part of an <a target="_blank" href="https://tools.ietf.org/html/rfc5861">extension</a> which were first described more than <a target="_blank" href="https://www.mnot.net/blog/2007/12/12/stale">10</a> years ago. Although their browser support is limited, <a target="_blank" href="https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today">some</a> popular CDNs have been supported them for more than 5 years!</p>
<p>Though stale-while-revalidate is pretty useful. As the name implies, it allows a cache to <em>“[...] immediately return a stale response while it revalidates it in the background, thereby hiding latency (both in the network and on the server) from clients”.</em></p>
<p>This caching extension proves really helpful for things like images, where reducing latency is critical for the user experience, and where having a stale version for a few seconds is often better than a painfully downloading image.</p>
<p>As for stale-if-error, it allows a cache to serve a stale version if the origin server returns a 5xx status code. This gives developers a chance to fix potential issues during a grace period where clients are shielded from irritating error pages.</p>
<p>Consider the case of a meteo third-party script. If the meteo server happens to be unreachable for a few minutes, it’s probably best to display a slightly outdated forecast during this lapse of time, than it is to see a portion of the page be blank (or a whole blank page if the code does not handle third-party scripts loading failures.</p>
<h3 id="heading-what-we-dont-know-yet">What we don’t know yet</h3>
<p>After examining these Cache-Control directives, we now understand how applications that are distributed on the web, tend to leverage HTTP caching mechanisms in multiple ways, depending on what they need.</p>
<p>Though what we don’t yet understand is what cache softwares actually do with the response they receive. They will most likely have to store it somewhere in order to retrieve it later. That’s the core idea of any caching system after all.</p>
<p>Under normal circumstances, this certainly looks like what we would call an implementation detail. It should be merely enough to know that resources are indeed stored some way. Yet in this case, learning just a little more is actually critical.</p>
<p>Neglecting the mechanisms that govern how caching softwares map objects from the HTTP responses space to their storage space can have really unexpected consequences, such as serving a brotli encoded Chinese document, to a user who does not understand Chinese, using a browser unable to decode brotli ¯_(ツ)_/¯</p>
<h2 id="heading-how-caches-store-and-retrieve-resources">How caches store and retrieve resources</h2>
<p>Albeit unlikely to happen, since most browsers can decode brotli - and since most people know how to 說中文 - the previous situation can still easily occur. To understand why this is the case, one must consider <em>how</em> caches store their representations.</p>
<p>By virtue of what they try to achieve, most caching softwares ought to be able to quickly retrieve simple text documents. To do so, a very simple yet powerful strategy is to use a key-value store. This strategy fits well in-memory representations. Therefore, the question one must answer when designing is the following: how to construct a cache key from an HTTP response?</p>
<p>What we are looking for here is a way to uniquely <em>identify</em> a <em>resource.</em> Conveniently, this is exactly why <a target="_blank" href="https://tools.ietf.org/html/rfc3986">URI</a>s - Uniform Resource Identifiers - were invented in the first place!</p>
<p>But URIs don’t tell the whole truth about resources. They never describe them entirely, if only for the fact that resources change over time.</p>
<p>Websites get rebranded, new content gets published and users update their profile. Granted, not for the same reasons or at the same frequency, though all resources will eventually change. In fact, the entire Conditional request specification is based on this sole observation: <em>nothing is permanent except</em> <a target="_blank" href="https://en.wikiquote.org/wiki/Heraclitus"><em>change</em></a><em>.</em></p>
<p>Philosophical quotes aside, there is, however, another time-independent reason why resources change. Indeed, any moment, resources may be available in multiple representations. This is why we have Content-Negociation.</p>
<p>The HTTP request headers Accept, Accept-Language, Accept-Encoding, Accept-Charset (and a few other headers who are not strictly speaking part of content negotiation) add another dimension on which representations can differ. As such, the problem of finding a good cache key becomes more complicated. Since all these representations share the same URI, caches must have a way to distinguish them in order to serve the right representation at each client, honoring content negotiation.</p>
<p>And since only origin servers know what different representations are available, it is again the origin server’s responsibility to indicate to a cache based on which headers it will generate a different representation. To do so, the origin servers must add a Vary <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#The_Vary_response_header">header</a> containing the value of the request headers that cause different representations to be generated.</p>
<p>When caches see a response coming from an origin server with, for instance, the header Vary**:** Accept-Language, it will examine the value of the Accept-Language header, such as fr-FR**,** and use this value to construct a more specific cache-key, perhaps like https://example.net/home.html_<strong>fr-FR<em>.</em></strong></p>
<p>The actual implementation strategy is of little importance to us. Altering the cache key might not even be the best way to do it. It somehow has to use the <em>value</em> of the header to differentiate representations.</p>
<p>The Vary header can actually point at more than one header, when resources are available in multiple representations. Selecting a cache key when multiple headers are involved is not really much more complicated than with only one header. The real problem when varying over multiple dimensions is the combinatorial <a target="_blank" href="https://en.wikipedia.org/wiki/Combinatorial_explosion">explosion</a>.</p>
<p>Unfortunately, there are no ways around this. If you are to cache and serve your resources in multiple representations, you have to pay the cost of a large storage. If you decide to lower your vary cardinality, some of your users will receive cache hits for responses that won’t match their requests.</p>
<p>On the other hand, if you vary properly on everything, and do not have enough storage space, chances are your users won’t be seeing cache hits anytime soon.</p>
<p>Now, it is important to know that this is only a problem if you decide to use a public cache, for which two different requests coming from two different users are running the same code, at the proxy level. If you decide to leverage the browser’s cache only, then you can skip the Vary header altogether and serve resources in as many representations as you want. This is because each browser’s cache will only cache representations matching the user’s preferences. This is good news!</p>
<p>But let’s not get ahead of ourselves just yet. As we said, caches use the <em>value</em> of the header as its input to generate a more specific cache key. But what is to say that all these values are well formatted ? Absolutely nothing! This is the rather inconvenient consequence of RFC <a target="_blank" href="https://tools.ietf.org/html/rfc2468">father</a>’s <a target="_blank" href="https://en.wikipedia.org/wiki/Robustness_principle">robustness principle</a>. HTTP servers are indeed very <em>liberal in what they accept</em>.</p>
<p>However there is hope.</p>
<p>Considering the case of an origin server that can only produce a representation in two different languages, caches must be able to regroup incoming Accept-Content values such as fr, fr-FR, fr_FR_.._ into something such as FR. Otherwise, just like before with the combinatorial explosion, the number of representations will explode, but in this case, for a misguided reason.</p>
<p>The process by which all these representations are regrouped is called <em>normalization</em> and is often done at the cache. Many caches offer configuration utilities or their own languages to deal with these situations. Sometimes, the functions are even already written, or snippets can easily be found on the Internet. The following pictures illustrates the process for the infamous User-Agent header.</p>
<p><img src="https://lh3.googleusercontent.com/YjJ67y4VX8-kzzVY78G6ICtdafwsx_M6_n9ce30Qv9jVYU3LrBXQrrxb13VkPjpm9WpBNs6JParrx5VEbtuKwKr5cTSUmMiXcayum2RTwRKho3c6R5iqmYj0lYqM5f6Klb2leIAo" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Fastly, a popular CDN, <a target="_blank" href="https://www.fastly.com/blog/best-practices-using-vary-header">sampled</a> 100 000 requests and found that the Accept-Encoding header was expressed in 44 different ways ! As for the User-Agent header, they found a shy of… 8000 different ones! Without normalization, chances are that the cache will never see any hit.</p>
<p>This wraps up the section about representation variation. At this point, we know how to instruct caches to store our resources, and have learned to leverage the Vary header to prevent accidents from happening when using public caches. We have now covered enough of the specification to be able to cache resources effectively.</p>
<h3 id="heading-common-misconceptions">Common misconceptions</h3>
<p>By now, you should have a thorough understanding of how HTTP caching works. Freshness control, resource’s representations and cache hits are no longer mysterious concepts to you. And if you start to feel empowered by all this knowledge, we have some good news for you: we’ve covered a large portion of the specification, and you now know pretty much all that’s necessary to be up and running.</p>
<p>But make no mistake. Caching <em>is</em> a complex topic.</p>
<p>Experience has shown us that, unless you’re dealing with it on a day-to-day basis, what may be crystal clear today will quickly turn into something rather blurry after a few weeks. Therefore, we decided to conclude this second article by dispelling two common misconceptions that are all too easy to make.</p>
<h3 id="heading-freshness-control-and-validation">Freshness-control and validation</h3>
<p>This might seem obvious after reading the previous sections but it is worth repeating many times. Freshness control and validation (<em>which we have slightly discussed in the beginning</em>) are two very distinct mechanisms that serve two very different purposes, and involve HTTP requests between different pieces.</p>
<ul>
<li><p>Freshness control always happen in a cache <strong>and is solely based on time</strong></p>
</li>
<li><p>Validations always happen in the origin server and are based <strong>both on time and on identifiers (ETags)</strong></p>
</li>
</ul>
<p>This is something we find important to remind ourselves. It means that once the cache has received temporal instructions, it can - and best believe it will - serve resources without ever contacting the origin server until the timer expires.</p>
<p>For instance, if your web application’s HTML file reaches a browser and the HTTP response happens to include the header Cache-Control: max-age=86400 the browser will happily serve the same version of your app for a day. In this case, the browser would serve it for one day without any possible action from you or anyone, except the user, if one ever decided to flush his browser’s cache.</p>
<p>If you’re thinking everyone can make mistakes, and one day is not so bad, well, brace yourself: the maximum max-age value is… 31536000 seconds! That is to say, <em>one year.</em> This is the reason why HTML files are very dangerous to cache like this, and should generally be declared with Cache-Control: no-cache.</p>
<h3 id="heading-freshness-and-most-recent-representation">Freshness and most recent representation</h3>
<p>Another misconception is to believe that cache hits and freshness have anything to do with having the last available version of a resource. This is what we all try to achieve, but one can never truly know if the resource it has been served from a cache is indeed the most up-to-date version. In fact, this holds true even in the absence of cache. It has to do with the nature of distributed applications: other people’s actions can change the things we are interacting with at any time.</p>
<p>When querying the state of the application, the ETag header must always be used to always let the server know what our current understanding of the application’s state is. And if it does not match the server’s, 409 Conflict are expected to be received on the client side.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Along this article, we have described how caching actually works. Now would be a good time to spin up a local dev server and fiddle around with these two core headers: Cache-Control and Vary to see them in action.</p>
<p>We started by giving an overview of how caching works, illustrating the four possible paths that a request can take : the happy path (cache hit) and the 3 possible ways to have a cache miss : empty cache, failed revalidation and successful revalidation. This overview alone gives the possibility to understand how complex caching topologies can fit together.</p>
<p>Then, we went deeper and looked at all the most useful Cache-Control headers, and clarified some subtle differences that are all easily missed.</p>
<p>We also looked at the Vary header and the fundamental difference between resources and representations, to avoid serving the wrong <em>representation</em> to the right client.</p>
<p>Finally, we took some time to review it all through the angle of common misconceptions you might encounter, and hopefully helped you to avoid them.</p>
<p>In the next article, we’ll apply all of this knowledge to set up a local lab environment in which we will set an innocent node.js app on fire with a load-testing tool, right before rescuing it with the help of a popular caching software.</p>
<p>Stay tuned!</p>
<h3 id="heading-to-go-further">To go further:</h3>
<p>The official specification about the material we covered (and other things)<br><a target="_blank" href="https://tools.ietf.org/html/rfc7234#section-5.3">https://tools.ietf.org/html/rfc7234#section-5.3</a></p>
<p>Google Web’s Fundamental<br><a target="_blank" href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#defining-optimal-cache-control-policy">https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#defining-optimal-cache-control-policy</a></p>
<p>About the Cache-Control header:<br><a target="_blank" href="https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Cache-Control">https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Cache-Control</a></p>
<p>About the Vary Header:<br><a target="_blank" href="https://www.smashingmagazine.com/2017/11/understanding-vary-header/">https://www.smashingmagazine.com/2017/11/understanding-vary-header/</a><br><a target="_blank" href="https://www.fastly.com/blog/best-practices-using-vary-header">https://www.fastly.com/blog/best-practices-using-vary-header</a><br><a target="_blank" href="https://www.fastly.com/blog/getting-most-out-vary-fastly">https://www.fastly.com/blog/getting-most-out-vary-fastly</a><br><a target="_blank" href="https://www.fastly.com/blog/understanding-vary-header-browser">https://www.fastly.com/blog/understanding-vary-header-browser</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
