Første gang jeg shippet et JSON-API på
Cloudflare Workers,
deployet jeg det fra laptopen kl. 23 og hadde det kjørende i 300+ datasentre før jeg var ferdig med teen.
Ingen Dockerfile, ingen Kubernetes-klynge, intet cold-start-drama. En enkelt wrangler deploy
og en 1,2 KB bundle. Den erfaringen er grunnen til at Workers har blitt mitt standardvalg for JSON-inn, JSON-ut-tjenester —
webhooks, proxyer, API-aggregatorer, edge-auth. Hvis 80% av backenden din er "parse JSON, gjør noe, returner JSON",
er dette artikkelen jeg skulle ønske jeg hadde da jeg begynte.
En Cloudflare Worker er i bunn og grunn en enkelt JavaScript- (eller TypeScript-) funksjon som kjører på V8-isolater i Cloudflares edge. Den mottar en Request, returnerer en Response, og har tilgang til standard Fetch-API. Har du brukt fetch() i en nettleser, kan du allerede 90% av runtime-en. Det du ikke kan er det lille settet med mønstre som skiller en leke-Worker fra en du faktisk kan kjøre i produksjon. Det er det denne artikkelen handler om.
Ditt første JSON-endepunkt
Her er det minste nyttige JSON-endepunktet. Det returnerer ett objekt med et tidsstempel og en
melding. Lagre det som src/index.ts i et Wrangler-prosjekt:
export default {
async fetch(request, env, ctx) {
const payload = {
message: 'Hello from the edge',
servedAt: new Date().toISOString(),
colo: request.cf?.colo ?? 'unknown',
};
return Response.json(payload);
},
};To ting å legge merke til. For det første: Response.json() er en statisk hjelper som serialiserer
objektet og setter Content-Type: application/json for deg. Ikke lag din egen
new Response(JSON.stringify(x)) med mindre du trenger en custom content type —
du kommer bare til å glemme headeren til slutt. For det andre: request.cf.colo forteller deg hvilket Cloudflare-
datasenter som serverer requesten. En request fra Berlin viser FRA,
fra Tokyo viser den NRT. Hele "edge"-pitchen i ett felt.
Parse en JSON-request-body
POST-endepunkter må lese en body. Fetch-API-et gir deg request.json(), som leser body-strømmen og parser den i ett kall:
export default {
async fetch(request) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const body = await request.json();
// body is now a regular JavaScript object
const { email, plan } = body;
return Response.json({
received: { email, plan },
ok: true,
});
},
};Ser rent ut, men denne koden har en bug du treffer innen 24 timer etter at du shipper den:
hvis klienten sender en tom body eller ugyldig JSON, kaster request.json()
en SyntaxError, Workeren din crasher, og Cloudflare returnerer en generisk 500.
Det er ikke svaret du vil ha foran kunder.
Håndtere ugyldig JSON — ikke la den bli 500
Pakk alltid body-parsing inn i try/catch og returner en skikkelig 400. Her er mønsteret jeg bruker i hver Worker:
async function readJson(request) {
try {
return { ok: true, data: await request.json() };
} catch (err) {
return {
ok: false,
error: 'Invalid JSON body',
detail: err.message,
};
}
}
export default {
async fetch(request) {
const result = await readJson(request);
if (!result.ok) {
return Response.json(result, { status: 400 });
}
const { email, plan } = result.data;
if (!email || !plan) {
return Response.json(
{ ok: false, error: 'email and plan are required' },
{ status: 422 },
);
}
return Response.json({ ok: true, email, plan });
},
};CORS for JSON-API-er
Hvis Workeren din skal kalles fra en nettleser på en annen origin — som er
normaltilfellet — trenger du
CORS-headers.
Nettlesere sender en OPTIONS-preflight før selve requesten for alt
utover en enkel GET. Håndter begge på ett sted:
const CORS_HEADERS = {
'Access-Control-Allow-Origin': 'https://app.example.com',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
export default {
async fetch(request) {
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
const data = { ping: 'pong', at: Date.now() };
return Response.json(data, { headers: CORS_HEADERS });
},
};Unngå Access-Control-Allow-Origin: * på noe som leser credentials
eller returnerer brukerdata. Det er en av de snarveiene som ser harmløs ut i dev og blir
til en sikkerhetshendelse i prod. Hardkod de origins du faktisk betjener, eller les dem fra en
allow-liste i env.
Videresende JSON til et oppstrøms-API
En av de mest vanlige bruksområdene for en Worker er som en tynn proxy: skjul en API-nøkkel, omform et svar, fjern felt en klient ikke trenger, eller sy sammen to oppstrøms-kall til ett. Her er en Worker som kaller en oppstrøms-tjeneste, plukker kun feltene vi bryr oss om, og returnerer en renere JSON-payload:
export default {
async fetch(request, env) {
const url = new URL(request.url);
const userId = url.searchParams.get('id');
if (!userId) {
return Response.json({ error: 'id required' }, { status: 400 });
}
const upstream = await fetch(
`https://api.internal.example.com/users/${userId}`,
{
headers: { 'Authorization': `Bearer ${env.UPSTREAM_TOKEN}` },
},
);
if (!upstream.ok) {
return Response.json(
{ error: 'upstream failed', status: upstream.status },
{ status: 502 },
);
}
const full = await upstream.json();
// Strip internal fields before returning to the client
const safe = {
id: full.id,
displayName: full.display_name,
avatarUrl: full.avatar_url,
joinedAt: full.created_at,
};
return Response.json(safe);
},
};To ting å være oppmerksom på. For det første: sjekk alltid upstream.ok
før du kaller .json() — en 500 fra oppstrøms vil ha HTML eller en feilside,
og å kalle .json() på den kaster på samme måte som enhver annen ugyldig JSON.
For det andre: hold hemmeligheter som UPSTREAM_TOKEN i Wrangler secrets
(wrangler secret put UPSTREAM_TOKEN) — aldri i wrangler.toml
og aldri committet til git.
Cache JSON-svar på edge
Når et oppstrøms-API er tregt eller dyrt, lar Cache-API-et deg memoisere JSON på edge. Hvert datasenter har sin egen cache, så den første brukeren i Frankfurt betaler oppstrøms-kostnaden, og de neste 10 000 får det på under 5 ms fra RAM i nærheten:
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
const cacheKey = new Request(request.url, request);
let response = await cache.match(cacheKey);
if (response) {
return response;
}
const upstream = await fetch('https://api.example.com/popular-items');
const data = await upstream.json();
response = Response.json(data, {
headers: {
'Cache-Control': 'public, max-age=60',
},
});
// Don't block the response on the cache write
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
},
};ctx.waitUntil() er den ikke-åpenbare delen. Uten den
awaites cache.put(), og svaret ditt venter på en diskskriving det egentlig
ikke trenger å bry seg om. waitUntil lar deg returnere svaret umiddelbart
mens runtime-en holder cache-skrivingen i live i bakgrunnen. Det er samme mønster
du ville brukt for analytics-beacons, logvideresending, alt som er fire-and-forget.
Lokal utvikling med Wrangler
Du trenger ikke en Cloudflare-konto for å iterere. Installer Wrangler, scaffold et prosjekt, og du får en lokal Workers-runtime som matcher produksjonen tett:
npm create cloudflare@latest my-json-api
cd my-json-api
npm run dev
# Worker is now live at http://localhost:8787
# Hit it from another terminal:
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","plan":"pro"}'Den lokale runtime-en bruker workerd, samme motor som Cloudflare kjører i produksjon.
Atferdene som skiller seg (KV-latens, cache-semantikk, request.cf-felt)
er godt dokumentert og biter deg sjelden for enkle JSON-API-er. Deploy med
wrangler deploy, og samme kode er live globalt på sekunder.
Nyttige verktøy for å bygge Worker-JSON-API-er
Noen verktøy jeg griper etter konstant når jeg bygger Workers som håndterer JSON: JSON Formatter for å pretty-printe et stygt oppstrøms-svar jeg prøver å reverse-engineere, JSON Validator når en POST-payload feiler og jeg trenger å vite nøyaktig hvor, JSON Path for å planlegge felt-plukke-logikken før jeg skriver den, og JSON Minifier når jeg vil sjekke om wire-størrelsen faktisk betyr noe for et gitt endepunkt.
Selve JSON-formatet er spesifisert i
RFC 8259
— verdt en skumlesning om du noen gang treffer en edge case som "tillater parseren min NaN?"
(svar: det burde den ikke). Cloudflares eget
Workers examples-galleri
har oppskrifter for JWT-verifisering, A/B-testing, HTML-omskriving og et dusin andre mønstre
når du har vokst ut av grunnprinsippene i denne artikkelen.
Oppsummering
Cloudflare Workers passer veldig godt for JSON-API-er — små, raske, globalt
distribuerte, og billige nok til at du kan la sideprosjekter kjøre. Happy path er
bare request.json() og Response.json(), men produksjonsveien
innebærer fire ekstra vaner: pakk body-parsing inn i try/catch, legg til CORS-headers bevisst,
sjekk upstream.ok før du parser proxyede svar, og bruk
ctx.waitUntil for cache-skrivinger og annet bakgrunnsarbeid. Får du de fire riktig,
vil du shippe Workers som holder seg oppe.