Service bindings¶
The Website talks to the Platform Worker (swapps-worker) through a Cloudflare
Service Binding named API_WORKER. This is a direct Worker-to-Worker call: it
never leaves Cloudflare's network, has no public HTTP hop, and incurs no egress.
Declaration¶
The binding is declared in wrangler.toml for the default, preview, and dev
environments:
[[services]]
binding = "API_WORKER"
service = "swapps-worker"
At runtime it is exposed on the Worker env as env.API_WORKER, a Fetcher with
a single fetch(request) method.
How a request reaches the binding during SSR¶
src/worker.ts passes the binding into the Vike page context so that server-side
+data.ts loaders can use it while rendering:
const pageContextInit = {
urlOriginal: url.href,
headersOriginal: Object.fromEntries(request.headers),
env: { BASE_URL, API_URL, NODE_ENV },
cloudflare: {
env: {
API_WORKER: env.API_WORKER,
},
},
};
The fetchData() helper and the HTTP fallback¶
All API calls go through fetchData() in src/utils/api.ts, which chooses the
transport based on what is available:
export async function fetchData(url, options = {}) {
const isDocker = import.meta.env.VITE_IS_DOCKER === "true";
const { pageContext, ...fetchOptions } = options;
if (isDocker) {
// Local Docker: plain HTTP to API_URL
return fetch(url, fetchOptions);
} else if (pageContext?.cloudflare?.env?.API_WORKER) {
// Production / preview: zero-latency Worker-to-Worker
const request = new Request(url, fetchOptions);
return pageContext.cloudflare.env.API_WORKER.fetch(request);
} else {
// Fallback: plain HTTP (e.g. binding unavailable)
console.error("Cloudflare API_WORKER not found. Falling back to standard fetch.");
return fetch(url, fetchOptions);
}
}
Resolution order:
| Condition | Transport |
|---|---|
VITE_IS_DOCKER === "true" |
Plain fetch() to API_URL (local Docker) |
pageContext.cloudflare.env.API_WORKER present |
Service Binding API_WORKER.fetch() |
| Neither | Plain fetch() fallback (logs an error) |
Note
Even on the binding path the call uses the full URL (built from
__API_URL__). The binding routes the request straight to swapps-worker
without a public DNS/HTTP round-trip, so the host portion is effectively
internal. Locally, API_URL points at http://localhost:8787 so the same
code path falls back to HTTP against a locally running Platform Worker.
What flows over the binding¶
Server-side +data.ts loaders call Platform endpoints through fetchData(..., { pageContext }):
| Use | Example endpoint (on the Platform Worker) |
|---|---|
| Blog listing | /api/posts?per_page=…&page=…&lang=… |
| Blog tags | /api/posts/tags?… |
| Blog categories | /api/posts/categories?… |
| Lead submission | /api/leads |
| Diagnostic / SEO analysis | /api/diagnostic/analyze, /api/seo/analyze |
| Support contact | /api/support/send |
Warning
Calls that originate client-side (after hydration, e.g. form submissions
triggered by the browser) cannot use the Service Binding — the binding only
exists inside the Worker. Those go out as normal HTTPS requests to the
Platform Worker's public URL (__API_URL__).
Related services (not bindings)¶
Two other services are reached with plain fetch, not Service Bindings:
- App —
fetchtoSUBSCRIPTIONS_API_URL(app.swapps.com) for subscription plans and checkout (src/api/subscriptions/). - AI —
fetchto/api/ai/*on the same origin, routed by Cloudflare to theswapps-aiWorker (src/api/ai.ts). See Lead generation.