Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
A first-party endpoint that proxies a third-party service. Keep external dependencies behind your own API surface for privacy, caching, swappability, and rate limiting.
A first-party endpoint that proxies a third-party service. Clients talk to your origin; your origin talks to the provider. One indirection layer yields privacy, caching, swappability, and rate limiting.
When a browser fetches from tile.openstreetmap.org, fonts.googleapis.com, or api.sendgrid.com directly, four concerns travel with it:
These are not individually severe, but they compound. GDPR scrutiny, a provider outage, or a pricing change turns a one-line fetch() into a migration project.
Route every third-party request through a first-party endpoint:
Client → /api/tiles/{z}/{x}/{y} → your edge → tile.openstreetmap.org/{z}/{x}/{y}
The client never contacts the third party. Your edge fetches upstream, caches the response, and serves it as a first-party resource.
Cache-Control, edge cache, KV, or R2.| Concern | Direct call | Service facade |
|---|---|---|
| User privacy | IP + referrer sent to third party | Only your server contacts upstream |
| Provider lock-in | URLs hardcoded in client | Swap backend transparently |
| Caching | Upstream headers only | Your cache policy + edge / KV |
| Rate limiting | None | Per-user, per-page, global |
| Offline / SSR | Client-only | Pre-fetch, pre-cache server-side |
| Monitoring | No visibility | Full request / response logging |
Any third-party asset or service is a candidate:
/go/Vanilla Breeze’s /go/ convention is one disciplined instance of this pattern. Every component that talks to a backend service uses a stable /go/{role} URL; the operator wires that URL to whatever provider currently fits. The broader pattern is the same — facades in front of third parties — but /go/ is opinionated about naming, reserved roles, and how service contracts are documented.
Think of the facade pattern as the general shape and /go/ as the VB-flavoured recipe. You can adopt the shape anywhere; adopt /go/ when you want VB’s components and tooling to plug in with zero configuration.
Future <geo-map> support will expose a tile-url attribute pointing to the first-party proxy:
<geo-map lat="40.7484" lng="-73.9857" provider="osm"></geo-map>
<geo-map lat="40.7484" lng="-73.9857" tile-url="/api/tiles/{z}/{x}/{y}"></geo-map>
The component doesn’t care where tiles come from — it only needs a URL template with {z}, {x}, and {y} placeholders.
A minimal Cloudflare Worker fronting OpenStreetMap tiles, with KV as the cache layer:
export default { async fetch(request, env) { const url = new URL(request.url); const match = url.pathname.match(/^\/api\/tiles\/(\d+)\/(\d+)\/(\d+)$/); if (!match) return new Response('Not found', { status: 404 }); const [, z, x, y] = match; const cacheKey = `tile:${z}:${x}:${y}`; // Check KV cache const cached = await env.TILE_CACHE.get(cacheKey, 'arrayBuffer'); if (cached) { return new Response(cached, { headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=86400', 'X-Cache': 'HIT', }, }); } // Fetch upstream const upstream = await fetch( `https://tile.openstreetmap.org/${z}/${x}/${y}.png`, { headers: { 'User-Agent': 'MyApp/1.0 (contact@example.com)' } } ); if (!upstream.ok) { return new Response('Upstream error', { status: 502 }); } const body = await upstream.arrayBuffer(); // Cache in KV (24h TTL) await env.TILE_CACHE.put(cacheKey, body, { expirationTtl: 86400 }); return new Response(body, { headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=86400', 'X-Cache': 'MISS', }, }); },};
The same shape works on any edge runtime — Vercel Edge Functions, Deno Deploy, Netlify Edge, Node behind Caddy. The five moving pieces are:
User-Agent, attribution, or usage caps. A caching proxy that reduces upstream load is usually welcomed; bulk scraping through your facade is still bulk scraping — read the tile usage policy or the equivalent before shipping.Use it when:
Skip it when:
/go/ Convention — VB’s opinionated take on this pattern with reserved role names.