Construisez des APIs sur Cloudflare Workers : edge runtime V8, latence sous 50ms mondiale, KV, D1, R2, Wrangler CLI et déploiement instantané sans serveur.
Pourquoi l'edge computing change tout
L'edge computing déplace le calcul au plus près de l'utilisateur final. Au lieu d'exécuter votre API dans une seule région (ex. eu-west-1 à Dublin), elle s'exécute dans le datacenter Cloudflare le plus proche du client — Paris, Tokyo, São Paulo ou Sydney. Résultat : une latence réseau divisée par 3 à 10, des temps de réponse inférieurs à 50 ms partout dans le monde, et une scalabilité quasi-infinie sans configuration.
Cloudflare Workers est l'implémentation la plus mature de cette idée. Lancé en 2017, le service repose sur le moteur V8 (le même que Chrome et Node.js) mais utilise une architecture radicalement différente : les V8 isolates. Plutôt que de démarrer un conteneur ou une VM par invocation, Cloudflare partage un seul processus V8 entre des milliers d'isolates légers — chacun complètement isolé en mémoire — ce qui élimine quasiment le cold start.
V8 isolates vs containers vs VMs
| Critère | V8 Isolates (Workers) | Containers (ECS, Cloud Run) | Micro-VMs (Lambda) |
|---|---|---|---|
| Cold start typique | ~5 ms | 500 ms – 2 s | 100 – 500 ms |
| Empreinte mémoire | ~3 Mo / isolate | 50 – 200 Mo | 30 – 100 Mo |
| Runtime | V8 + Web APIs (≠ Node) | Image Docker complète | Node.js, Python, Go, etc. |
| Localisation | 300+ POPs mondiaux | Régions fixes | Régions fixes (Lambda@Edge limité) |
| Limite CPU / requête | 10 ms (free) / 30 s (paid) | Sans limite (pay per ms) | 15 minutes max |
Workers vs Lambda@Edge vs Vercel Edge Functions
| Critère | Cloudflare Workers | AWS Lambda@Edge | Vercel Edge Functions |
|---|---|---|---|
| Free tier | 100k req/jour | Aucun | 500k exécutions/mois |
| Cold start | ~5 ms | 30 – 100 ms | ~10 ms (basé sur Workers) |
| Storage natif | KV + D1 + R2 + Durable Objects | DynamoDB Global Tables | Vercel KV (Upstash) + Postgres |
| Runtime | V8 isolates | Node.js limité (1 Mo, no env) | V8 isolates (sur Workers) |
| Pricing au-delà du free | 5 $/mois + 10M req inclus | 0,60 $ / 1M req + 0,00005 $/GB-s | 20 $/mois (Pro) + facturation usage |
| Idéal pour | APIs mondiales, edge logic | CloudFront viewers/origin | Next.js + Vercel integrations |
fs, pas de modules natifs C++, pas de process.env direct. À la place, vous utilisez les Web APIs standards : fetch, Request, Response, crypto.subtle, WebSocket. La plupart des bibliothèques modernes (Hono, drizzle-orm, jose, hono/jwt) sont compatibles, mais express, multer ou sharp ne fonctionnent pas.
Setup Wrangler et premier Worker
Wrangler est la CLI officielle Cloudflare. Elle gère la création de projets, le développement local (avec un runtime workerd identique à la production), les déploiements et l'administration des bindings (KV, D1, R2). Aucune authentification requise pour démarrer en local — uniquement pour déployer.
Création d'un nouveau projet
# Création interactive — choisissez "Hello World Worker" + TypeScript
npm create cloudflare@latest -- my-edge-api
# Réponses recommandées :
# - What would you like to start with? -> Hello World Worker
# - Which language do you want to use? -> TypeScript
# - Do you want to use git? -> Yes
# - Do you want to deploy your application? -> No (on configure d'abord)
cd my-edge-api
ls
# .gitignore package.json src/ tsconfig.json wrangler.jsonc worker-configuration.d.ts
Anatomie d'un Worker minimal
// src/index.ts — point d'entrée du Worker
export default {
// fetch() est appelé pour CHAQUE requête HTTP entrante
// env contient les bindings (secrets, KV, D1) déclarés dans wrangler.jsonc
// ctx permet d'enregistrer des tâches asynchrones après réponse (waitUntil)
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
const url = new URL(request.url);
// Routing simple basé sur le pathname
if (url.pathname === '/health') {
return Response.json({ status: 'ok', region: request.cf?.colo ?? 'unknown' });
}
if (url.pathname === '/echo' && request.method === 'POST') {
const body = await request.json();
return Response.json({ received: body });
}
return new Response('Not Found', { status: 404 });
},
} satisfies ExportedHandler<Env>;
Configuration Wrangler
// wrangler.jsonc — configuration du Worker
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-edge-api", // nom du Worker (sous-domaine workers.dev)
"main": "src/index.ts", // point d'entrée TypeScript
"compatibility_date": "2026-04-01", // pinning des features V8 (figées à cette date)
"compatibility_flags": ["nodejs_compat"], // active les polyfills Node (Buffer, process)
"observability": {
"enabled": true // active les logs en production
},
"vars": {
"ENVIRONMENT": "production" // variable d'environnement publique (non-secrète)
}
}
Lancer le serveur de développement
# Démarrer le Worker en local sur http://localhost:8787
# wrangler dev utilise workerd — le MÊME runtime qu'en production
npx wrangler dev
# Tester le endpoint
curl http://localhost:8787/health
# {"status":"ok","region":"unknown"}
curl -X POST http://localhost:8787/echo \
-H "Content-Type: application/json" \
-d '{"hello":"world"}'
# {"received":{"hello":"world"}}
Premier déploiement
# Login (ouvre le navigateur sur dash.cloudflare.com)
npx wrangler login
# Déploie sur le sous-domaine workers.dev (gratuit, HTTPS automatique)
npx wrangler deploy
# Sortie attendue :
# Total Upload: 0.85 KiB / gzip: 0.42 KiB
# Uploaded my-edge-api (1.23 sec)
# Deployed my-edge-api triggers (3.45 sec)
# https://my-edge-api.<account>.workers.dev
# Current Version ID: 8a3f2c1e-...
workers.dev est gratuit et inclut HTTPS, HTTP/3 et la protection DDoS Cloudflare. Vous pouvez le désactiver dans Workers & Pages → Settings → workers.dev et ne servir que via custom domain pour des raisons de SEO ou de conformité.
API REST avec le framework Hono
Écrire un routeur manuellement avec URL.pathname devient vite verbeux. Hono (本) est un framework ultra-léger (~14 Ko) optimisé pour l'edge — Workers, Deno, Bun, Lambda, Node.js. Sa surface API ressemble à Express mais sans aucune dépendance Node, et avec une gestion native de TypeScript pour les params de routes et les bindings.
Installation
# Ajouter Hono au projet
npm install hono
# Pour la validation et la sérialisation
npm install zod @hono/zod-validator
API REST de tâches — exemple complet
// src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { bearerAuth } from 'hono/bearer-auth';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
// Bindings injectés par wrangler (KV, secrets) — typés depuis worker-configuration.d.ts
type Bindings = {
TASKS_KV: KVNamespace;
API_TOKEN: string; // secret, voir section 5
};
const app = new Hono<{ Bindings: Bindings }>();
// Middlewares globaux
app.use('*', logger());
app.use('*', cors({
origin: ['https://app.example.com', 'http://localhost:4200'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
maxAge: 600,
}));
// Auth Bearer pour les routes /api/*
app.use('/api/*', async (c, next) => {
const auth = bearerAuth({ token: c.env.API_TOKEN });
return auth(c, next);
});
// Schéma de validation Zod réutilisable
const TaskSchema = z.object({
title: z.string().min(1).max(120),
done: z.boolean().default(false),
due: z.string().datetime().optional(),
});
// GET /api/tasks — liste paginée
app.get('/api/tasks', async (c) => {
// KV.list() retourne les clés ; on récupère les valeurs en parallèle
const { keys } = await c.env.TASKS_KV.list({ prefix: 'task:', limit: 100 });
const tasks = await Promise.all(
keys.map(k => c.env.TASKS_KV.get(k.name, 'json'))
);
return c.json({ count: tasks.length, tasks });
});
// POST /api/tasks — création avec validation
app.post(
'/api/tasks',
zValidator('json', TaskSchema),
async (c) => {
const data = c.req.valid('json');
const id = crypto.randomUUID();
const task = { id, ...data, createdAt: new Date().toISOString() };
await c.env.TASKS_KV.put(`task:${id}`, JSON.stringify(task));
return c.json(task, 201);
}
);
// GET /api/tasks/:id — détail avec param typé
app.get('/api/tasks/:id', async (c) => {
const id = c.req.param('id');
const raw = await c.env.TASKS_KV.get(`task:${id}`);
if (!raw) return c.json({ error: 'not found' }, 404);
return c.json(JSON.parse(raw));
});
// DELETE /api/tasks/:id
app.delete('/api/tasks/:id', async (c) => {
const id = c.req.param('id');
await c.env.TASKS_KV.delete(`task:${id}`);
return c.body(null, 204);
});
// Public health check (pas d'auth)
app.get('/health', (c) => c.json({ status: 'ok' }));
export default app;
Tester avec curl
# Liste vide au départ
curl https://my-edge-api.example.workers.dev/api/tasks \
-H "Authorization: Bearer $API_TOKEN"
# {"count":0,"tasks":[]}
# Créer une tâche
curl -X POST https://my-edge-api.example.workers.dev/api/tasks \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Déployer Workers","due":"2026-05-15T18:00:00Z"}'
# {"id":"a1b2...","title":"Déployer Workers","done":false,...}
RegExpRouter compile les routes en une expression régulière unique au démarrage de l'isolate. Résultat : le matching d'URL prend ~0,02 ms même avec 100+ routes — négligeable face aux 5 ms de cold start V8.
Stockage : KV, D1 et R2
Les Workers seuls sont stateless. Pour persister des données, Cloudflare propose une suite de services natifs accessibles via des bindings — des objets injectés dans env sans configuration réseau ni IAM. Trois piliers : KV (clé-valeur eventually-consistent), D1 (SQLite serverless) et R2 (object storage S3-compatible).
Workers KV — clé-valeur global, eventually-consistent
KV est répliqué sur 300+ POPs : les lectures sont ultra-rapides (cache local au datacenter, ~10 ms) mais les écritures se propagent en 60 secondes maximum. Idéal pour : config, feature flags, sessions, cache de réponses API.
# Créer un namespace KV (génère un ID à coller dans wrangler.jsonc)
npx wrangler kv namespace create TASKS_KV
# 🌀 Creating namespace with title "my-edge-api-TASKS_KV"
# ✨ Success!
# Add the following to your configuration file:
# [[kv_namespaces]]
# binding = "TASKS_KV"
# id = "9c8d7e6f5a4b3c2d1e0f..."
# Pour un environnement de preview (wrangler dev)
npx wrangler kv namespace create TASKS_KV --preview
// wrangler.jsonc — déclaration des bindings KV
{
"name": "my-edge-api",
"main": "src/index.ts",
"compatibility_date": "2026-04-01",
"kv_namespaces": [
{
"binding": "TASKS_KV", // nom dans env.TASKS_KV
"id": "9c8d7e6f5a4b3c2d1e0f...", // ID production
"preview_id": "1a2b3c4d5e6f7g8h9i0j..." // ID local/preview
}
]
}
// Utilisation depuis le Worker
// PUT avec TTL (auto-expiration en 1h)
await env.TASKS_KV.put('session:abc123', JSON.stringify({ userId: 42 }), {
expirationTtl: 3600, // secondes
metadata: { ip: '1.2.3.4' }, // jusqu'à 1024 bytes de méta
});
// GET typé directement en JSON
const session = await env.TASKS_KV.get<{ userId: number }>('session:abc123', 'json');
// LIST paginé
const { keys, list_complete, cursor } = await env.TASKS_KV.list({
prefix: 'session:',
limit: 1000,
cursor: undefined, // pour la pagination
});
D1 — SQLite serverless
D1 est SQLite hébergé par Cloudflare avec réplication read-only sur les POPs. Idéal pour les schémas relationnels (joins, transactions, SQL standard) et compatible avec drizzle-orm, Kysely, Prisma. Coût : 5 GB inclus dans le plan Free.
# Créer une base D1
npx wrangler d1 create my-edge-db
# ✅ Successfully created DB 'my-edge-db'
# [[d1_databases]]
# binding = "DB"
# database_name = "my-edge-db"
# database_id = "c5e8f1a2-..."
# Appliquer un schéma SQL
cat > schema.sql <<'EOF'
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
EOF
# Exécuter en local (preview) puis en production
npx wrangler d1 execute my-edge-db --file=schema.sql --local
npx wrangler d1 execute my-edge-db --file=schema.sql --remote
// Requêtes D1 depuis Hono — paramétrées (anti-injection)
import { Hono } from 'hono';
type Bindings = { DB: D1Database };
const app = new Hono<{ Bindings: Bindings }>();
// SELECT avec params bindés
app.get('/users/:email', async (c) => {
const email = c.req.param('email');
const result = await c.env.DB
.prepare('SELECT id, email, name, created_at FROM users WHERE email = ?')
.bind(email)
.first(); // .all() pour plusieurs lignes
return result ? c.json(result) : c.notFound();
});
// INSERT avec récupération du dernier ID
app.post('/users', async (c) => {
const { email, name } = await c.req.json();
const { meta } = await c.env.DB
.prepare('INSERT INTO users (email, name) VALUES (?, ?)')
.bind(email, name)
.run();
return c.json({ id: meta.last_row_id, email, name }, 201);
});
// Batch atomique (transaction implicite)
const stmts = users.map(u =>
c.env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind(u.email, u.name)
);
await c.env.DB.batch(stmts);
R2 — object storage S3-compatible
R2 stocke des fichiers binaires (images, vidéos, backups) avec une API S3 standard et zéro frais de bande passante sortante — un avantage majeur sur S3 (où l'egress coûte 0,09 $/GB).
# Créer un bucket
npx wrangler r2 bucket create my-edge-uploads
# Configuration dans wrangler.jsonc
# "r2_buckets": [
# { "binding": "UPLOADS", "bucket_name": "my-edge-uploads" }
# ]
// Upload d'un fichier multipart/form-data
app.post('/upload', async (c) => {
const form = await c.req.formData();
const file = form.get('file') as File;
if (!file) return c.json({ error: 'no file' }, 400);
const key = `${Date.now()}-${file.name}`;
await c.env.UPLOADS.put(key, file.stream(), {
httpMetadata: {
contentType: file.type,
cacheControl: 'public, max-age=31536000',
},
customMetadata: { uploadedBy: 'user-42' },
});
return c.json({ key, size: file.size });
});
// Streaming d'un fichier (download)
app.get('/files/:key', async (c) => {
const obj = await c.env.UPLOADS.get(c.req.param('key'));
if (!obj) return c.notFound();
const headers = new Headers();
obj.writeHttpMetadata(headers);
headers.set('etag', obj.httpEtag);
return new Response(obj.body, { headers });
});
Secrets, custom domain et Cloudflare Access
Variables d'environnement publiques (vars)
// wrangler.jsonc — vars sont visibles dans le code source du Worker (non secrètes)
{
"vars": {
"ENVIRONMENT": "production",
"FEATURE_BETA": "false",
"LOG_LEVEL": "info"
},
"env": {
"staging": {
"vars": { "ENVIRONMENT": "staging", "LOG_LEVEL": "debug" }
}
}
}
Secrets — chiffrés et invisibles
Les vars apparaissent en clair dans le dashboard. Pour des valeurs sensibles (API keys, tokens, mots de passe DB), utilisez wrangler secret. Les secrets sont chiffrés au repos et jamais visibles après écriture.
# Ajouter un secret (saisie interactive — pas dans l'historique shell)
npx wrangler secret put API_TOKEN
# ✔ Enter a secret value: ▌ (saisie masquée)
# 🌀 Creating the secret for the Worker "my-edge-api"
# ✨ Success! Uploaded secret API_TOKEN
# Ajouter en mode CI (depuis stdin)
echo -n "$STRIPE_SECRET_KEY" | npx wrangler secret put STRIPE_SECRET
# Lister (sans afficher les valeurs)
npx wrangler secret list
# Supprimer
npx wrangler secret delete API_TOKEN
# Cibler un environnement
npx wrangler secret put DATABASE_URL --env staging
// Utilisation côté Worker — les secrets sont des strings dans env
type Bindings = {
API_TOKEN: string;
STRIPE_SECRET: string;
};
app.post('/charge', async (c) => {
const stripe = new Stripe(c.env.STRIPE_SECRET); // chiffré, jamais loggé
// ...
});
Custom domain et SSL automatique
Le sous-domaine *.workers.dev est pratique pour le dev mais peu professionnel. Sur un domaine déjà géré par Cloudflare DNS, l'ajout d'un custom domain prend 30 secondes — certificat SSL auto-géré (Universal SSL Cloudflare), HTTP/3 et zéro config Nginx/CDN.
# Option 1 : via wrangler.jsonc — Workers Routes
{
"routes": [
{
"pattern": "api.example.com/*",
"zone_name": "example.com"
}
]
}
# Option 2 : via Workers Custom Domains (recommandé)
# Dashboard → Workers & Pages → my-edge-api → Settings → Triggers → Add Custom Domain
# Saisir api.example.com — DNS et SSL configurés automatiquement
Cloudflare Access — protection Zero Trust
Pour des endpoints internes (admin, staging, métriques), Cloudflare Access ajoute un SSO devant le Worker — sans modifier le code. Authentification via Google Workspace, GitHub, Okta, OTP email. Gratuit jusqu'à 50 utilisateurs.
// Validation du JWT Cloudflare Access côté Worker
import { Hono } from 'hono';
import { verify } from 'hono/jwt';
const app = new Hono();
const TEAM_DOMAIN = 'mycompany.cloudflareaccess.com';
const AUD_TAG = 'a1b2c3d4...'; // visible dans Access policy → Settings
app.use('/admin/*', async (c, next) => {
const jwt = c.req.header('Cf-Access-Jwt-Assertion');
if (!jwt) return c.json({ error: 'unauthorized' }, 401);
// Récupère les clés publiques Access (cache via env.KV recommandé)
const certs = await fetch(`https://${TEAM_DOMAIN}/cdn-cgi/access/certs`)
.then(r => r.json());
// Vérification JWT avec aud = AUD_TAG
// ... (utiliser jose pour la validation complète)
await next();
});
- Secrets uniquement via
wrangler secret— jamais dansvarsni dans le code - CORS restrictif — liste blanche des origines, pas de
*sur les endpoints authentifiés - Rate limiting — règle Cloudflare WAF (100 req/min par IP) ou middleware
hono-rate-limiter
Déploiement, Git integration et CI/CD
Environnements multiples (dev / staging / production)
// wrangler.jsonc — chaque env hérite de la config racine puis surcharge
{
"name": "my-edge-api",
"main": "src/index.ts",
"compatibility_date": "2026-04-01",
"vars": { "LOG_LEVEL": "info" },
"env": {
"staging": {
"name": "my-edge-api-staging", // nom Worker distinct
"vars": { "LOG_LEVEL": "debug" },
"kv_namespaces": [
{ "binding": "TASKS_KV", "id": "<staging-kv-id>" }
],
"routes": [{ "pattern": "api-staging.example.com/*", "zone_name": "example.com" }]
},
"production": {
"kv_namespaces": [
{ "binding": "TASKS_KV", "id": "<prod-kv-id>" }
],
"routes": [{ "pattern": "api.example.com/*", "zone_name": "example.com" }]
}
}
}
# Déployer en staging
npx wrangler deploy --env staging
# Déployer en production
npx wrangler deploy --env production
# Vérifier la version active
npx wrangler deployments list --env production
Workflow GitHub Actions
# .github/workflows/deploy.yml
name: Deploy Cloudflare Worker
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm test # vitest avec @cloudflare/vitest-pool-workers
- run: npx wrangler deploy --dry-run # vérifie le bundling sans déployer
deploy-staging:
needs: test
if: github.ref == 'refs/heads/staging'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- name: Deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: npx wrangler deploy --env staging
deploy-production:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production # protection GitHub (review obligatoire)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: npx wrangler deploy --env production
Git integration native (Cloudflare Pages-style)
Depuis 2025, le dashboard Workers permet de connecter directement un repo GitHub/GitLab. Chaque push déclenche un preview deployment avec une URL unique par PR — comme Vercel ou Netlify, mais pour les Workers.
# Activer via dashboard
# Workers & Pages → my-edge-api → Settings → Build → Connect to Git
# - Repository : example/my-edge-api
# - Production branch : main
# - Build command : npm install
# - Deploy command : npx wrangler deploy
# Chaque PR génère :
# https://<pr-number>.my-edge-api.<account>.workers.dev
Rollback instantané
# Lister les versions historiques
npx wrangler deployments list
# Rollback vers une version spécifique (atomique, sans rebuild)
npx wrangler rollback --message "Rollback v3 (bug auth)" \
--version-id 8a3f2c1e-...
# Validation du rollback
curl https://api.example.com/health
- Secrets configurés via
wrangler secret put(jamais dans le code ni les vars) - Custom domain ajouté avec SSL automatique
- CORS restrictif (liste blanche d'origines)
- Rate limiting WAF activé (100 req/min/IP minimum)
- Observability activé dans
wrangler.jsoncpour les logs - Tests Vitest avec
@cloudflare/vitest-pool-workersdans le pipeline CI - Environnements
stagingetproductionséparés (KV, D1 distincts) - Stratégie de rollback testée (
wrangler rollbackvalidé) - Monitoring : alerte sur taux d'erreur 5xx > 1 % via Cloudflare Notifications
Limitations, pricing et cas d'usage
Limites techniques à connaître
| Ressource | Free plan | Workers Paid (5 $/mois) |
|---|---|---|
| Requêtes par jour | 100 000 | 10 millions inclus, puis 0,30 $/M |
| CPU par requête | 10 ms | 30 secondes |
| Mémoire | 128 Mo | 128 Mo |
| Taille du bundle Worker | 3 Mo (gzippé : 1 Mo) | 10 Mo (gzippé) |
| Sous-requêtes par invocation | 50 | 1000 |
| KV opérations/jour | 100k reads + 1k writes | 10M reads + 1M writes inclus |
| D1 stockage | 5 Go + 5M reads/jour | 5 Go inclus + scale |
| R2 stockage | 10 Go + 1M ops Class A | 0,015 $/GB-mois (egress gratuit) |
Estimation de coût — APIs typiques
- Side project / blog API : 10–50k requêtes/jour → 0 $ (free tier)
- SaaS B2B 100 clients : 500k requêtes/jour → 5 $/mois (plan Paid suffit)
- API publique 5M req/jour : 150M req/mois → 5 $ + 42 $ ≈ 47 $/mois
- Lambda équivalent (5M req/jour) : ≈ 90 $/mois requêtes + 60 $/mois GB-s ≈ 150 $+/mois
Cas d'usage idéaux
- APIs REST/GraphQL avec audience mondiale (latence < 50 ms partout)
- Backends de PWA et mobile apps (auth, profils, sessions)
- Webhooks et intégrations Stripe / GitHub / Slack à fort volume
- Edge middleware : A/B testing, geo-routing, header injection, auth tokens
- Image transformation à la demande (avec Workers Images ou R2 + cache)
- Cron jobs distribués (
scheduled()handler) — gratuit jusqu'à 5 crons - Real-time avec WebSockets via Durable Objects (chat, collaboration, présence)
Quand préférer une autre plateforme
- Charges CPU lourdes (ML inference, encoding vidéo, PDF lourd) — préférer Cloud Run / Lambda avec GPU
- Modules natifs Node (sharp, puppeteer, canvas C++) — non supportés sur V8 isolates
- Très long-running (> 30 s par requête) — préférer Cloud Run ou ECS Fargate
- Compliance stricte avec résidence de données régionale — Workers répartit les données globalement (D1 en preview pour les régions spécifiques)
caches.default sur les réponses GET idempotentes. Une réponse cachée n'est pas facturée comme requête Worker — elle est servie directement par le CDN. Sur des APIs publiques (catalogues, contenus statiques), le ratio cache/origin peut atteindre 95 %, divisant la facture Workers d'autant.
// Exemple : cache automatique des GET pendant 60 secondes
app.get('/products', async (c) => {
const cacheKey = new Request(c.req.url, c.req.raw);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (response) return response; // hit cache → pas facturé Worker
const products = await c.env.DB.prepare('SELECT * FROM products').all();
response = c.json(products.results);
response.headers.set('Cache-Control', 'public, max-age=60');
c.executionCtx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
});
Conclusion
Cloudflare Workers redéfinit le serverless en plaçant l'exécution à 50 ms de chaque utilisateur de la planète. Combiné à Hono pour le routing, KV pour les données de session, D1 pour le relationnel et R2 pour le stockage objet, on obtient une stack complète, type-safe, déployée en quelques secondes via Wrangler — pour 0 à 5 $/mois sur la plupart des projets.
Les limitations (pas de Node.js complet, 30 s CPU max, V8 isolates only) imposent des choix d'architecture, mais elles sont précisément ce qui garantit le cold start de ~5 ms et la scalabilité automatique. Pour 80 % des APIs modernes — auth, CRUD, webhooks, edge logic — Workers représente aujourd'hui le meilleur rapport latence/coût/simplicité face à Lambda, Cloud Run et Vercel.
npm create cloudflare@latestpour scaffolder un Worker TypeScript- Ajouter Hono pour un routing type-safe et léger (~14 Ko)
- Utiliser KV pour les sessions, D1 pour le relationnel, R2 pour les fichiers
- Configurer secrets via
wrangler secret put— jamais dansvars - Activer Cloudflare Access devant les endpoints internes (gratuit ≤ 50 users)
- Pipeline GitHub Actions avec
--dry-runen PR et déploiement par branch - Mettre en cache les GET idempotents avec
caches.defaultpour réduire les coûts - Surveiller les cold starts et le 99e percentile via observability + Cloudflare Notifications