Edge API Gateway & Serverless Subscription Backend
Traditional API architectures place a server — or a cluster of servers — between the client and its data. Those servers live in a region. Users far from that region pay a latency penalty on every request. Scaling requires provisioning, and idle hours cost money.
I built a two-layer edge backend that eliminates the server entirely. An API gateway and a subscription management backend run as Cloudflare Workers — V8 isolates deployed simultaneously across 300+ edge locations worldwide. There is no origin server to scale, no region to pick, and no idle infrastructure cost.
Layer 1: The API Gateway
Every inbound request hits the gateway Worker first. Its job is singular and non-negotiable: verify identity before forwarding anything.
│
▼
[ Cloudflare Gateway Worker ]
│
├──► Extract Authorization: Bearer <firebase_jwt>
│
├──► Fetch JWKS from Firebase ──► Verify RS256 Signature
│
├──► Validate: expiry, issuer, audience
│
├──► Inject X-Authenticated-UID header
│
▼
[ Internal Microservice ] (never sees raw JWT — only the verified UID)
Why JWKS instead of shared secrets?
Firebase rotates its signing keys automatically. A gateway that hardcodes a public key will break when Firebase rotates. By fetching the JWKS endpoint and selecting the correct key by kid (Key ID) from the JWT header, the gateway remains valid across rotations with zero operator intervention. The Jose library handles the cryptographic verification; the Worker handles the routing logic.
Identity propagation without re-verification
Once the gateway verifies the JWT, it strips the token and injects the verified user ID as an X-Authenticated-UID header. Downstream services never receive an unverified token — they receive a pre-verified identity claim. This eliminates per-service JWT verification and keeps the auth boundary at the edge.
Layer 2: The Subscription Backend
The backend Worker handles subscription state for a Flutter application — managing active subscriptions, trial periods, and Stripe payment webhooks. The architectural constraint was strict: every read must be fast enough to not block app startup, and every write must be atomic enough to never leave the user in an inconsistent state.
Cloudflare KV as the single source of truth
KV (Key-Value store) is globally replicated with low read latency. The subscription state for each user is a single KV key. The read path is a direct KV lookup — no database query, no JOIN, no cache miss. The write path is a single KV put, triggered by a verified Stripe webhook event.
Stripe Webhook → State Machine
Stripe fires webhook events for every subscription lifecycle transition. The Worker consumes these events and maps them to KV writes. The state machine is intentionally simple — complexity in payment state machines is where bugs hide.
Request Lifecycle: End-to-End
Dead Endpoint Hygiene
APIs accumulate dead routes over time. Most backends return 404 for missing routes — but 404 implies the resource might exist elsewhere or in future. For removed endpoints, the correct HTTP status is 410 Gone: the resource existed here and has been permanently removed. Clients and CDN caches can treat 410 as a signal to stop retrying.
Local Development & Testing Strategy
Cloudflare Workers run on V8 isolates, not Node.js. The runtime exposes Web APIs (Fetch, Cache, KV bindings) but not Node.js globals. Testing required a Vitest-based suite with mocked Worker bindings rather than a local Express server masquerading as a Worker.
Gateway Test Strategy
The gateway test suite mocks the JWKS endpoint and tests: valid JWT acceptance, expired JWT rejection, wrong issuer rejection, missing header rejection, and correct UID header injection. Local mode bypasses JWT verification (non-production only).
Backend Test Strategy
The subscription Worker tests mock KV bindings and Stripe webhook payloads. Each state transition is tested in isolation. Webhook signature verification is tested with known test secrets from Stripe's test mode.
Engineering Tradeoffs
KV consistency model
Cloudflare KV is eventually consistent — a write in one region propagates to other regions within seconds, not milliseconds. For subscription state, this means a user who just upgraded on the US-East edge might see their old state from the EU edge for a few seconds. This is an acceptable tradeoff for a subscription product — users don't upgrade mid-second and expect simultaneous global consistency. If this were a financial ledger, the architecture would use Cloudflare Durable Objects (strongly consistent) instead.
Stateless design eliminates drift
Because the Workers hold zero in-memory state between requests (V8 isolates are ephemeral), there is no risk of stale state accumulating over time. Every request starts from the authoritative KV store. There is no cache invalidation problem because there is no cache — just an eventually consistent global KV.