Cloud & Déploiement angularforall.com

- Cloudflare Workers : edge computing pour APIs rapides

Cloudflare Workers Edge-Computing Serverless V8 Wrangler Hono Cloudflare-Kv Cloudflare-D1 Cloudflare-R2 Durable-Objects Api-Rest Deploiement Devops
Cloudflare Workers : edge computing pour APIs rapides

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
Le piège du runtime : Workers n'est pas Node.js. Pas de 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-...
Le sous-domaine 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,...}
Performance Hono : Le routeur Hono 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 });
});
Durable Objects : pour des cas nécessitant une cohérence forte (compteurs, locks, WebSockets stateful, salles de chat), Cloudflare propose les Durable Objects. Chaque DO a une instance unique par ID, transactionnel et persistant. Plan payant uniquement (à partir du plan Workers Paid 5 $/mois).

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();
});
Sécurité — le triptyque obligatoire en production :
  1. Secrets uniquement via wrangler secret — jamais dans vars ni dans le code
  2. CORS restrictif — liste blanche des origines, pas de * sur les endpoints authentifiés
  3. 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
Checklist mise en production Workers :
  • 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.jsonc pour les logs
  • Tests Vitest avec @cloudflare/vitest-pool-workers dans le pipeline CI
  • Environnements staging et production séparés (KV, D1 distincts)
  • Stratégie de rollback testée (wrangler rollback validé)
  • 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

Workers brille pour :
  • 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)
Astuce coût : Activez le cache Cloudflare avec 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.

Récapitulatif — démarrer aujourd'hui :
  • npm create cloudflare@latest pour 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 dans vars
  • Activer Cloudflare Access devant les endpoints internes (gratuit ≤ 50 users)
  • Pipeline GitHub Actions avec --dry-run en PR et déploiement par branch
  • Mettre en cache les GET idempotents avec caches.default pour réduire les coûts
  • Surveiller les cold starts et le 99e percentile via observability + Cloudflare Notifications

Partager