Wrangler Explained: Deploying Edge Applications to Cloudflare
Deploying backend logic close to users used to require managing distributed server clusters or dealing with slow cold starts on traditional container lambdas. Cloudflare Workers changed this by running code on V8 isolates distributed across hundreds of edge locations.
Wrangler is the official CLI used to develop, test, and deploy Workers, Pages, and edge storage primitives.
Here is a practical guide to using Wrangler effectively in production.
Setting Up wrangler.toml for Multiple Environments
A clean wrangler.toml configuration defines your entry points, bindings, and environment overrides:
name = "api-gateway"
main = "src/index.ts"
compatibility_date = "2026-03-01"
compatibility_flags = ["nodejs_compat"]
# Production environment (default)
[vars]
ENVIRONMENT = "production"
ALLOWED_ORIGIN = "https://yourdomain.com"
# Key-Value storage binding
[[kv_namespaces]]
binding = "CACHE_KV"
id = "a1b2c3d4e5f67890123456789abcdef0"
# Cloudflare D1 (Serverless SQLite) binding
[[d1_databases]]
binding = "DB"
database_name = "prod-db"
database_id = "f9e8d7c6-b5a4-3210-9876-543210fedcba"
# Staging environment override
[env.staging]
name = "api-gateway-staging"
vars = { ENVIRONMENT = "staging", ALLOWED_ORIGIN = "https://staging.yourdomain.com" }
[[env.staging.kv_namespaces]]
binding = "CACHE_KV"
id = "b2c3d4e5f6a17890123456789abcdef1"
Writing an Edge Worker with TypeScript
With nodejs_compat enabled, you get standard Node-compatible APIs alongside native web primitives (Request, Response, fetch):
export interface Env {
CACHE_KV: KVNamespace;
DB: D1Database;
ENVIRONMENT: string;
ALLOWED_ORIGIN: string;
API_SECRET_KEY: string; // Injected via wrangler secret
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
// Handle CORS preflight
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": env.ALLOWED_ORIGIN,
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
}
// Edge-cached endpoint
if (url.pathname === "/v1/summary" && request.method === "GET") {
const cacheKey = `summary:${url.searchParams.get("id") || "default"}`;
// 1. Check Cloudflare KV (sub-15ms edge lookup)
const cached = await env.CACHE_KV.get(cacheKey);
if (cached) {
return new Response(cached, {
headers: {
"Content-Type": "application/json",
"X-Cache": "HIT",
"Access-Control-Allow-Origin": env.ALLOWED_ORIGIN,
},
});
}
// 2. Query Cloudflare D1 serverless database on cache miss
const id = url.searchParams.get("id") || "1";
const record = await env.DB.prepare(
"SELECT id, title, content FROM posts WHERE id = ?"
).bind(id).first();
if (!record) {
return new Response(JSON.stringify({ error: "Not found" }), {
status: 404,
headers: { "Content-Type": "application/json" }
});
}
const payload = JSON.stringify(record);
// 3. Populate edge KV asynchronously without blocking user response
ctx.waitUntil(env.CACHE_KV.put(cacheKey, payload, { expirationTtl: 3600 }));
return new Response(payload, {
headers: {
"Content-Type": "application/json",
"X-Cache": "MISS",
"Access-Control-Allow-Origin": env.ALLOWED_ORIGIN,
},
});
}
return new Response("Not Found", { status: 404 });
},
};
Managing Secrets Securely
Never commit API keys, database credentials, or JWT signing secrets to wrangler.toml.
Store secrets using Wrangler's encrypted store:
# Upload production secret
npx wrangler secret put API_SECRET_KEY
# Upload secret for staging environment
npx wrangler secret put API_SECRET_KEY --env staging
For local development, create a .dev.vars file in your project root (and add it to .gitignore):
ENVIRONMENT=local
API_SECRET_KEY=dev_secret_local_12345
Local Development and CI/CD Deployment
1. Local Testing
Run the local emulation engine with hot-reloading:
npx wrangler dev --port 8787
This spins up a local Miniflare instance that accurately emulates Workers, KV, and D1 SQLite storage on your machine.
2. GitHub Actions Deployment Workflow
Deploying on every merge to main:
name: Deploy Cloudflare Worker
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Deploy to Cloudflare
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
Deploying to edge infrastructure with Wrangler provides sub-30ms cold starts worldwide and simplifies hosting APIs and gateways without running dedicated servers.
Comments
Threaded replies are enabled. Upvote useful ideas.