MeshWorld India Logo MeshWorld.
Netlify Edge-Computing Serverless Caching Performance 5 min read

Optimizing Netlify Blobs for Edge Functions: Lightweight In-Memory Caching

Scarlett
By Scarlett
Optimizing Netlify Blobs for Edge Functions: Lightweight In-Memory Caching

Netlify Blobs is a solid key-value store for unstructured data in serverless setups. The problem? Fetching those blobs from Edge Functions adds a network hop — 50–200ms every single time. Do that on every request and you’ve just killed the whole point of running on the edge.

This guide shows you how to drop a lightweight in-memory cache into your edge function and use HTTP conditional headers so your execution time drops by 80% or more.

TL;DR: The Edge Caching Strategy
  • The Problem: Querying Netlify Blobs from edge execution environments introduces a network hop, adding 50–200ms of database latency per API request.
  • The Solution: Set up a local in-memory variable cache to store blobs during the edge container’s active lifecycle, and use conditional HTTP headers.
  • Immediate Gain: Sub-millisecond response times for subsequent API requests, minimal data transfer costs, and faster edge cold starts.

Why the Latency?

Netlify Edge Functions run on Deno’s V8 isolate network — code executes on servers closest to your visitors. Fast. But Netlify Blobs live in centralized storage buckets:

text
Edge Function Latency Loop:
[Visitor Web Request] --> [Edge Function (Tokyo)] -->|Network Hop (50-200ms)|--> [Netlify Blobs Store (Central)]
                                                                                       |
                                                                             Returns Blob Data Payload

Every await blobs.get('my-key') triggers a network hop. That’s 50–200ms of extra latency, which basically cancels out the speed you get from edge computing. The fix: cache inside the edge container itself.


Step 1: A Lightweight In-Memory Cache

Edge containers stay alive across requests. That means a global variable can hold your cached blobs in memory.

Before (no cache):

Every request hits the blob store directly:

typescript
import { getStore } from "@netlify/blobs";

export default async (request: Request) => {
  const store = getStore("my-store");
  // ❌ Triggers an expensive network hop on every request
  const data = await store.get("config-data", { type: "json" });
  return Response.json(data);
};

After (with cache):

typescript
import { getStore } from "@netlify/blobs";

// 1. Declare a global memory cache variable
interface CacheEntry {
  data: any;
  expiresAt: number;
}
const memoryCache = new Map<string, CacheEntry>();

export default async (request: Request) => {
  const store = getStore("my-store");
  const cacheKey = "config-data";
  const now = Date.now();

  // 2. Check if a valid cache entry exists in memory
  const cached = memoryCache.get(cacheKey);
  if (cached && cached.expiresAt > now) {
    // ✅ Returns data in under 1 millisecond
    return Response.json(cached.data, {
      headers: { "X-Cache-Status": "HIT" }
    });
  }

  // 3. Fallback to network fetch if cache is empty or expired
  const freshData = await store.get(cacheKey, { type: "json" });
  
  // 4. Update the global cache with a 5-minute Time-to-Live (TTL)
  memoryCache.set(cacheKey, {
    data: freshData,
    expiresAt: now + 5 * 60 * 1000 // 5 minutes in milliseconds
  });

  return Response.json(freshData, {
    headers: { "X-Cache-Status": "MISS" }
  });
};

Step 2: Conditional HTTP Headers

In-memory cache helps on the server side. HTTP conditional headers help on the client side. Combine them and you’re barely moving data.

Using ETag hashes, you can tell the edge function to return a 304 Not Modified when nothing changed — zero payload transfer:

typescript
export default async (request: Request) => {
  const clientETag = request.headers.get("if-none-match");
  
  // Retrieve the latest metadata hash of the blob
  const store = getStore("my-store");
  const metadata = await store.getMetadata("config-data");
  const currentETag = metadata?.etag || "default-hash";

  // If the client's cached ETag matches the current blob hash
  if (clientETag === currentETag) {
    // ✅ Return instant response with 0 byte payload transfer
    return new Response(null, {
      status: 304,
      headers: { "ETag": currentETag }
    });
  }

  const data = await store.get("config-data");
  return new Response(data, {
    headers: { 
      "Content-Type": "application/json",
      "ETag": currentETag
    }
  });
};

Step 3: Benchmark It

Deploy the optimized function, open Chrome’s Network panel, and see for yourself:

  • Cache MISS (first request): 80–120ms — the edge isolate connects to the blob bucket.
  • Cache HIT (subsequent requests): 1–3ms — data served straight from memory.
  • HTTP 304 (client-side): 0 bytes over the network. Mobile users will thank you.

If you are currently scaling your system’s front-end foundations to handle high-performance workloads, review my guide on Building a Design System: Tokens, Components & Docs to align your UI elements with modern performance standards.


Frequently Asked Questions

What happens to the in-memory cache when Deno Edge Functions cold start?

Cold start = new V8 container, fresh memory, empty cache. First request will be a MISS. After that, the cache fills and stays warm for all subsequent requests on that container.

How do I manually invalidate the in-memory cache?

You can’t easily clear memory across dozens of distributed containers. Set a conservative TTL (1–5 minutes) or append a version hash like ?v=2 to your requests.

Is Netlify Blobs secure for storing sensitive user information?

Yes — data is encrypted at rest with scoped access tokens. Just make sure your handler endpoints have proper auth checks before serving sensitive blobs.