9/18 | Validate feature ideas earlier with AI-driven prototypes

Announcing Visual Copilot - Figma to production in half the time

Builder.io
Builder.io
Contact sales

9/18 | Validate feature ideas earlier with AI-driven prototypes

Announcing Visual Copilot - Figma to production in half the time

Builder’s Content API is designed for speed and resilience, but performance depends on how requests are structured.

This guide explains how to maximize cache efficiency, avoid patterns that bypass protection, and set up an optional 0-trust architecture with your own caching layer

This guide shows how to:

  • avoid patterns that bypass protective caching
  • keep cache hit rates high for speed and resiliency
  • optional: adopt an intermediate cache for mission-critical workloads

cachebust=true bypasses protective edge caching. Reserve it for controlled maintenance tasks only, such as webhook-triggered prefetch jobs. Remove it from all production user requests.

Builder’s CDN uses stale-while-revalidate and stale-if-error. That lets caches serve immediately from a recent copy while updating in the background, and it shields users during origin hiccups.

Builder’s CDN cache keys include the full URL. Fewer unique URLs means more cache hits and faster, safer responses.

Good examples: audience=menswear, isNewUser=true, region=us, plan=premium

Avoid per-visitor or highly unique values like visitorId, timestamps, random tokens, long free-text.

GET https://cdn.builder.io/api/v3/content/page
  ?apiKey=YOUR_PUBLIC_KEY
  &userAttributes.audience=menswear
  &userAttributes.isNewUser=true
  • Keep attribute value sets small and well defined.
  • Prefer enums or booleans over free-text.
  • Keep parameter order consistent to improve de-dupe in intermediate caches.
  • Do not include PII in query params.

For businesses where even an occasional moment of degraded performance can be business-critical, you may want to consider an intermediate cache.

To have zero reliance on Builder at request time, you can place your own cache between your app and Builder. All production traffic then hits your cache only.

Your cache serves the last known good response and refreshes asynchronously. If Builder is slow or returns errors, users keep getting the cached copy until refresh succeeds.

  • Cache key: include model name plus only low-cardinality userAttributes
  • TTL: set a reasonable max-age and allow stale reads during refresh
  • Refresh: revalidate asynchronously in the background
  • On error: serve stale from your cache
sub vcl_backend_response {
  set beresp.ttl = 5m;
  set beresp.grace = 60m;   # serve stale up to 60m while revalidating
}
sub vcl_backend_response {
  set beresp.ttl = 300s;
  set beresp.stale_if_error = 3600s;
  set beresp.stale_while_revalidate = 60s;
}
export default {
  async fetch(req, env, ctx) {
    const url = new URL(req.url);
    // Build a low-cardinality cache key
    const key = new Request(url.toString(), { method: 'GET' });
    const cache = caches.default;

    // 1) Try cache
    let res = await cache.match(key);
    if (res) {
      // 2) Kick off background refresh
      ctx.waitUntil(refreshAndPut(key));
      return res;
    }

    // 3) Fetch origin and populate cache
    res = await fetch(key).catch(() => null);
    if (res && res.ok) {
      ctx.waitUntil(cache.put(key, res.clone()));
      return res;
    }

    // Optional: read a last-known-good from KV or R2
    return new Response('Service temporarily unavailable', { status: 503 });
  }
}

async function refreshAndPut(key) {
  const cache = caches.default;
  const fresh = await fetch(key).catch(() => null);
  if (fresh && fresh.ok) await cache.put(key, fresh);
}

To use your own intermediate layer, switch SDK fetches (builder.get(...), fetchOneEntry(...)) to direct REST calls to our Content API. The SDKs are thin wrappers; using HTTP directly makes it easy to route through your cache.

const url = new URL('https://cdn.builder.io/api/v3/content/page');
url.searchParams.set('apiKey', process.env.BUILDER_PUBLIC_KEY);
url.searchParams.set('userAttributes.audience', 'menswear');
url.searchParams.set('userAttributes.isNewUser', 'true');

const res = await fetch(url.toString(), {
  headers: { 'Accept': 'application/json' },
});
const data = await res.json();
// Store in your cache, render, or transform as needed
GET /api/v3/content/page?apiKey=...&userAttributes.audience=menswear&userAttributes.isNewUser=true
GET ...&userAttributes.visitorId=3d7f9c1a-...   # extreme cardinality
GET ...&cachebust=true                          # bypasses protective cache
  • Remove cachebust=true from runtime requests
  • Use a small set of stable userAttributes values
  • Add an intermediate cache if you need 0 reliance on our APIs at runtime
Was this article helpful?

Product

Visual CMS

Theme Studio for Shopify

Sign up

Login

Featured Integrations

React

Angular

Next.js

Gatsby

Get In Touch

Chat With Us

Twitter

Linkedin

Careers

© 2020 Builder.io, Inc.

Security

Privacy Policy

Terms of Service

Get the latest from Builder.io

By submitting, you agree to our Privacy Policy

  • Fusion

  • Publish

  • Product Updates

  • Figma to Code Guide

  • Headless CMS Guide

  • Headless Commerce Guide

  • Composable DXP Guide

Security

Privacy Policy

SaaS Terms

Compliance

Cookie Preferences

Gartner Cool Vendor 2024