Cloudflare Workers: Deploy Globally at the Edge Without Managing Servers
Traditional deployment forces you to pick a region, and everyone far from it pays in latency. A user in Lagos hitting a server in Virginia waits on the speed of light, no matter how well your code is optimized. Edge computing flips that model, instead of running your code in one place and making users come to it, you run it everywhere and let each request be handled close to the person who made it.
Cloudflare Workers is one of the most popular ways to do this. In this guide, you'll learn how Workers works, what the surrounding platform offers, and how to build and deploy a real API globally with a single command.
What Is Edge Computing?
Edge computing moves computation from a few centralized data centers to many locations distributed near end users. The practical benefits are:
- Lower latency. Requests are handled by a nearby location instead of crossing oceans.
- Better resilience. Traffic is spread across a global network rather than concentrated in one region.
- Less infrastructure work. There are no regions to choose, no instances to size, and no load balancers to configure.
Edge platforms are a natural fit for APIs, authentication, personalization, A/B testing, redirects, and anything that benefits from a fast response close to the user.
How Cloudflare Workers Works
Workers runs your JavaScript, TypeScript, or WebAssembly code on Cloudflare's global network, which spans more than 335 data centers worldwide. When a request arrives at any of those locations, your code runs there.
The key difference from traditional serverless platforms is V8 isolates. Workers runs on the same V8 engine that powers Chrome and Node.js, but instead of spinning up a container or virtual machine for each function, it creates a lightweight isolate inside an already-running runtime. Because the runtime is already loaded, cold starts are virtually eliminated, and a single runtime instance can host thousands of isolates, each with its own memory space.
That architecture shapes how you write code:
- Compute per request. Your Worker's
fetch()handler runs whenever a request arrives. - Distributed execution. Any two requests may hit different instances in different locations, so you can't rely on shared in-memory state.
- Isolates can be evicted. Cloudflare may spin an isolate down at any time, so treat each invocation as potentially fresh.
The Workers Platform: More Than Functions
A function alone isn't an application. Cloudflare pairs Workers with storage and coordination services that you reach through bindings, which are objects injected into your code through the env parameter. You don't manage connection strings or credentials in code for these.
| Need | Service | Best for |
|---|---|---|
| Relational data | D1 | SQLite-based SQL database, joins, transactions |
| Fast key-value reads | KV | Config, feature flags, sessions, cached responses |
| Files and blobs | R2 | S3-compatible object storage with zero egress fees |
| Strongly consistent state | Durable Objects | Counters, locks, chat rooms, real-time coordination |
| Background jobs | Queues | Async and retryable work off the request path |
| AI inference | Workers AI | Running models at the edge |
Choose based on access patterns: SQL joins belong in D1, large files in R2, globally readable key-value data in KV, and shared mutable coordination in Durable Objects.
Getting Started: Your First Worker in Minutes
You need Node.js and a free Cloudflare account.
1. Scaffold a Project
npm create cloudflare@latest edge-api
cd edge-api
Pick the "Hello World" Worker template and TypeScript when prompted. This generates a project with Wrangler (Cloudflare's CLI) already configured.
2. Understand the Entry Point
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
return new Response("Hello from the edge!");
},
} satisfies ExportedHandler<Env>;
Every request to your Worker invokes fetch(). Return a Response and you're done.
3. Run It Locally
npx wrangler dev
This starts a local development server (typically on port 8787) that simulates the Workers runtime, with hot reload.
4. Deploy Globally
npx wrangler deploy
That's the entire deployment process. Your code is live across Cloudflare's network on a *.workers.dev URL, and you can attach a custom domain from the dashboard or your config.
Building a Real Example: A Cached Product API
Let's build an API that reads from a D1 database and caches responses in KV.
Create the Resources
npx wrangler d1 create edge-db
npx wrangler kv namespace create CACHE
Each command prints an ID. Add them to your Wrangler config:
// wrangler.jsonc
{
"name": "edge-api",
"main": "src/index.ts",
"compatibility_date": "2026-10-01",
"d1_databases": [
{ "binding": "DB", "database_name": "edge-db", "database_id": "<your-d1-id>" }
],
"kv_namespaces": [
{ "binding": "CACHE", "id": "<your-kv-id>" }
]
}
Create the Schema with a Migration
npx wrangler d1 migrations create edge-db init
Add your SQL to the generated file:
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price_cents INTEGER NOT NULL
);
INSERT INTO products (name, price_cents) VALUES
('Wireless Keyboard', 4999),
('USB-C Hub', 2999);
Apply it locally first, then to production:
npx wrangler d1 migrations apply edge-db --local
npx wrangler d1 migrations apply edge-db --remote
Write the Worker
// src/index.ts
export interface Env {
DB: D1Database;
CACHE: KVNamespace;
API_KEY: string;
}
const json = (body: string, cache: "HIT" | "MISS", status = 200) =>
new Response(body, {
status,
headers: { "content-type": "application/json", "x-cache": cache },
});
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (request.method === "GET" && url.pathname === "/api/products") {
const cacheKey = "products:all";
// 1. Try the cache first
const cached = await env.CACHE.get(cacheKey);
if (cached) return json(cached, "HIT");
// 2. Fall back to the database
const { results } = await env.DB
.prepare("SELECT id, name, price_cents FROM products ORDER BY id LIMIT 50")
.all();
const body = JSON.stringify(results);
// 3. Populate the cache without delaying the response
ctx.waitUntil(env.CACHE.put(cacheKey, body, { expirationTtl: 60 }));
return json(body, "MISS");
}
if (request.method === "POST" && url.pathname === "/api/products") {
if (request.headers.get("authorization") !== `Bearer ${env.API_KEY}`) {
return new Response("Unauthorized", { status: 401 });
}
const { name, price_cents } = await request.json<{ name: string; price_cents: number }>();
// Always bind parameters. Never concatenate user input into SQL.
await env.DB
.prepare("INSERT INTO products (name, price_cents) VALUES (?, ?)")
.bind(name, price_cents)
.run();
await env.CACHE.delete("products:all");
return new Response("Created", { status: 201 });
}
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;
A few things worth noticing:
ctx.waitUntil()lets the cache write finish after the response is sent, so users don't wait on it.- Parameter binding (
.bind()) protects against SQL injection. - Secrets stay out of config. Set the API key with
npx wrangler secret put API_KEY, and use a.dev.varsfile for local development.
Deploy
npx wrangler deploy
Your API is now running worldwide.
Gotchas Every Workers Developer Should Know
Most Workers bugs come from carrying over habits from long-running servers.
- Don't store mutable state in global scope. Requests may land on different isolates, and isolates can be evicted. Use KV, D1, or Durable Objects for anything that must persist.
- KV is eventually consistent. Writes can take up to about a minute to propagate globally, and it isn't designed for frequent writes to the same key. If you need read-after-write guarantees, use D1 or Durable Objects.
- Know your limits. Workers have CPU time, memory, request size, and subrequest limits that vary by plan. Billing is based on CPU time, so time spent waiting on I/O generally doesn't count against you. Check the current limits page before committing to an architecture.
- Not everything from Node.js is available. Workers implements standard web APIs (
fetch,Request,Response,crypto), and Node.js compatibility is available through a compatibility flag, but some libraries that assume a full Node environment or native modules won't run. - Use the right config per environment. Apply D1 migrations with an explicit
--localor--remoteflag, and use separate bindings or environments for staging and production. - Avoid holding large objects in memory. Stream large payloads and keep big data in R2 or D1 rather than in the Worker.
Best Practices for Production
- Cache aggressively, invalidate deliberately. Combine KV or the Cache API with short TTLs and explicit purges on writes.
- Keep Workers small and fast. Smaller bundles start faster. Tree-shake dependencies and avoid heavy libraries.
- Offload slow work. Use Queues for emails, webhooks, and other retryable tasks instead of blocking the request.
- Handle errors explicitly. Return meaningful status codes and log failures. Wrangler and the dashboard provide logs and observability.
- Use gradual deployments. Roll out new versions to a fraction of traffic first and promote once metrics look healthy.
- Test locally with
wrangler devbefore every deploy, including your D1 queries against the local database.
What Does It Cost?
Workers has a free tier that's generous enough to run a real, database-backed side project at no cost, and D1, KV, R2, and Durable Objects all include free usage as well. Paid plans scale with usage, and because billing is CPU-time based, I/O-heavy workloads (like proxying APIs) are often cheaper than on request-duration platforms. R2's zero egress fees also remove one of the largest hidden costs in cloud storage.
Pricing and limits change, so confirm current numbers on Cloudflare's pricing page and use their calculator before estimating production costs.
When Workers Isn't the Right Fit
Edge computing isn't a universal answer. Consider something else when:
- You need long-running processes, heavy CPU-bound computation, or large in-memory datasets
- You depend on native modules or libraries that require a full Node.js environment
- Your data lives in a single-region database and every request needs multiple round trips to it (tools like Hyperdrive can help, but latency to a distant database can erase the edge advantage)
- You need strict control over exactly where code executes for compliance reasons
For many teams the answer is hybrid, Workers at the front for routing, auth, and caching, with heavier services behind it.
Final Thoughts
Cloudflare Workers removes most of the operational work of going global. There are no regions to pick, no servers to patch, and no capacity to plan, and deployment is one command. The tradeoff is learning to think in stateless, request-scoped terms and picking the right storage primitive for each job.
Start small: deploy a single Worker, add D1 or KV when you need state, and expand from there. Within an afternoon you can have an API serving users from a location near them, wherever they are.
About the author
- I am a passionate and results-driven Software Developer & DevOps Engineer with a strong interest in building a clean and maintainable codebase. With hands-on experience across the full development lifecycle, from planning and design to implementation and deployment, I specialize in developing clean performant code and automating robust infrastructure workflows. I thrive at the intersection of software engineering and DevOps practices, where I can streamline processes, improve reliability, and accelerate delivery cycles. I’m always seeking new opportunities to impact meaningful projects, work with talented teams, and continue growing at the forefront of technology.
Comments
Join the discussionNo comments yet. Be the first to comment.

