Cloudflare Workers lets you run JavaScript at the network edge — in data centers physically close to your users, rather than in a central server. For Indian users, this means the difference between 400ms response times from a US server and 20ms from Cloudflare’s Mumbai or Chennai edge nodes.
This guide covers what Cloudflare Workers actually is, when to use it, and how to build real things with it — specifically for Indian developers and businesses.
What Is Edge Computing?
Traditional web architecture: User in Jabalpur → Request travels to your server in Mumbai (or worse, Singapore/US) → Response travels back. Round trip: 80–400ms depending on server location.
Edge computing: User in Jabalpur → Request hits Cloudflare’s nearest node (Mumbai, ~10ms away) → Code runs at the edge → Response served immediately. Round trip: 15–30ms.
Cloudflare Workers is a serverless edge platform where your JavaScript/TypeScript code runs in Cloudflare’s network of 300+ data centers worldwide — including multiple nodes in India.
When Edge Computing Makes Sense
| Use Case | Edge? | Why |
|---|---|---|
| API responses with user-specific data | ✅ Yes | Runs close to user, fast |
| Authentication/JWT validation | ✅ Yes | No round-trip to origin needed |
| A/B testing and feature flags | ✅ Yes | Serve different content without origin |
| Geolocation-based redirects | ✅ Yes | Cloudflare knows user location natively |
| Rate limiting | ✅ Yes | Block before reaching your server |
| Image transformation | ✅ Yes | Resize on the fly at the edge |
| Complex DB queries | ❌ No | DB is still in one location |
| File uploads | ⚠️ Partial | Route to R2, process at edge |
| Long-running processes | ❌ No | Workers have 30s CPU limit |
Step 1 — Setup
npm install -g wrangler
wrangler login
# Create a new Worker project
wrangler init my-worker --type javascript
cd my-worker
// wrangler.toml
name = "softcrony-api"
main = "src/index.ts"
compatibility_date = "2026-07-01"
compatibility_flags = ["nodejs_compat"]
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-namespace-id"
[[d1_databases]]
binding = "DB"
database_name = "softcrony-db"
database_id = "your-d1-database-id"
Step 2 — Basic Worker
// src/index.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Route based on pathname
switch (url.pathname) {
case '/api/health':
return Response.json({ status: 'ok', region: request.cf?.colo });
case '/api/geo':
return this.handleGeo(request);
default:
return new Response('Not Found', { status: 404 });
}
},
handleGeo(request: Request): Response {
// Cloudflare provides geolocation data natively
const cf = request.cf;
return Response.json({
country: cf?.country,
city: cf?.city,
region: cf?.region,
timezone: cf?.timezone,
isp: cf?.asOrganization,
// Automatically detect Indian users
isIndia: cf?.country === 'IN',
});
},
};
Step 3 — Geolocation-Based Content for India
// Show prices in INR for Indian users, USD for others
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const country = request.cf?.country;
const isIndia = country === 'IN';
// Fetch from origin
const response = await fetch('https://api.softcrony.com/products');
const products = await response.json() as Product[];
// Transform prices based on location
const localizedProducts = products.map(product => ({
...product,
price: isIndia
? { amount: product.price_inr, currency: 'INR', symbol: '₹' }
: { amount: product.price_usd, currency: 'USD', symbol: '$' },
// Show GST info for Indian users
tax: isIndia ? { rate: 18, label: 'GST 18%' } : null,
}));
return Response.json(localizedProducts, {
headers: {
'Cache-Control': 'public, max-age=300',
'X-Served-From': request.cf?.colo ?? 'unknown',
'X-User-Country': country ?? 'unknown',
},
});
},
};
Step 4 — Rate Limiting at the Edge
// Rate limit before request hits your Laravel server
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get('CF-Connecting-IP') ?? 'unknown';
const key = `rate_limit:${ip}`;
// Check rate limit from KV
const requests = parseInt(await env.CACHE.get(key) ?? '0');
if (requests >= 100) {
return new Response('Rate limit exceeded', {
status: 429,
headers: {
'Retry-After': '60',
'X-RateLimit-Limit': '100',
'X-RateLimit-Remaining': '0',
},
});
}
// Increment counter
await env.CACHE.put(key, String(requests + 1), {
expirationTtl: 60 // Reset after 60 seconds
});
// Pass to origin
const response = await fetch(request);
return new Response(response.body, {
...response,
headers: {
...Object.fromEntries(response.headers),
'X-RateLimit-Limit': '100',
'X-RateLimit-Remaining': String(99 - requests),
},
});
},
};
Step 5 — JWT Authentication at Edge
import { jwtVerify } from 'jose';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Only protect API routes
const url = new URL(request.url);
if (!url.pathname.startsWith('/api/')) {
return fetch(request);
}
// Skip auth for public routes
const publicRoutes = ['/api/login', '/api/register', '/api/health'];
if (publicRoutes.includes(url.pathname)) {
return fetch(request);
}
// Validate JWT at edge — before hitting Laravel
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const token = authHeader.slice(7);
try {
const secret = new TextEncoder().encode(env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret);
// Add user info to headers for Laravel
const modifiedRequest = new Request(request, {
headers: {
...Object.fromEntries(request.headers),
'X-User-Id': String(payload.sub),
'X-User-Email': String(payload.email),
'X-Tenant-Id': String(payload.tenant_id),
},
});
return fetch(modifiedRequest);
} catch {
return Response.json({ error: 'Invalid token' }, { status: 401 });
}
},
};
Step 6 — Cloudflare D1 (Edge Database)
D1 is Cloudflare’s SQLite-based edge database — query data without leaving the edge network:
// Create D1 database
wrangler d1 create softcrony-db
// Apply schema
wrangler d1 execute softcrony-db --file=./schema.sql
// schema.sql
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price_inr INTEGER NOT NULL,
price_usd INTEGER NOT NULL,
status TEXT DEFAULT 'active',
created_at TEXT DEFAULT (datetime('now'))
);
// Query D1 from Worker
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { results } = await env.DB.prepare(
'SELECT * FROM products WHERE status = ? ORDER BY created_at DESC LIMIT 20'
)
.bind('active')
.all();
return Response.json(results);
},
};
Step 7 — Deploy
# Deploy to production
wrangler deploy
# Test locally first
wrangler dev
# View logs in real time
wrangler tail
Performance Impact for Indian Users
Real numbers from a client project — a product listing API moved from a DigitalOcean server in Singapore to Cloudflare Workers:
| Location | Before (Singapore) | After (Edge) | Improvement |
|---|---|---|---|
| Mumbai | 180ms | 18ms | 90% faster |
| Delhi | 210ms | 22ms | 90% faster |
| Jabalpur | 240ms | 25ms | 90% faster |
| Chennai | 160ms | 15ms | 91% faster |
| Kolkata | 230ms | 28ms | 88% faster |
For the right use cases — API responses, authentication, rate limiting, geolocation — edge computing delivers 10x latency improvements for Indian users. This directly impacts conversion rates and user experience on mobile networks.
If you want to implement Cloudflare Workers for your application or need edge computing architecture for an Indian-market product, our team at Softcrony can design and implement it.
Leave a comment