Use Cases (Mis à jour: 01/06/2026)

Fonctions serverless avec Claude Code : Lambda et Workers

Guide pratique pour creer des fonctions serverless avec Claude Code : plateforme, secrets, idempotence, tests et deploy.

Fonctions serverless avec Claude Code : Lambda et Workers

Les fonctions serverless sont de petits morceaux de code executes pour chaque evenement ou requete HTTP, sans serveur applicatif a maintenir en permanence. Elles sont utiles pour les webhooks, les petites APIs, les traitements d’image ou de CSV, et les routes edge. Mais elles demandent tout de meme une vraie discipline sur les timeouts, retries, secrets, permissions, logs et couts.

Claude Code aide parce qu’il peut produire dans le meme contexte le handler, le fixture d’evenement, les tests, les notes de deploiement et la checklist de revue. Le bon modele n’est pas de le laisser deployer seul. Le bon modele est : clarifier les exigences, choisir la plateforme, reproduire localement l’evenement, separer configuration et secrets, concevoir l’idempotence, tester les echecs, puis faire valider exposition et couts par un humain.

Gardez les docs officielles a portee de main : AWS Lambda Documentation, Lambda avec Node.js, Cloudflare Workers development and testing et Workers get started guide. En complement, consultez le guide AWS Lambda, le guide Cloudflare Workers, le guide de developpement API et le guide de gestion des secrets.

Choisir Le Bon Cas D’usage

Le serverless fonctionne bien quand le travail est court, declenche par un evenement, et peut etre rejoue sans dommage.

Cas d’usagePourquoi c’est adapteClaude Code peut preparerRevue humaine obligatoire
Webhook paiement ou formulaireUne requete devient un evenement durableVerification de signature, fixture, reponses d’erreurSecrets, doublons, replay
Entree image resize ou CSVLe travail lourd part vers storage ou queueValidation, job ID, logs JSONTaille fichiers, timeout, cleanup
Petite API JSON internePas besoin d’un serveur permanentHandler, tests, routeAuth, CORS, exposition, rate limit
Redirect/cache edgeReponse proche de l’utilisateurRoute Worker, headers cache, notes rolloutPurge cache, donnees personnelles, SEO
flowchart LR
  A[Prompt d'exigences] --> B[Choisir Lambda ou Workers]
  B --> C[Reproduire localement]
  C --> D[Separer env et secrets]
  D --> E[Idempotence et retry]
  E --> F[Tests]
  F --> G[Deploy dev]
  G --> H[Logs et cleanup]

Prompt De Depart

Cree une fonction serverless minimale en Node.js.

Objectif:
- Gerer POST /orders et retourner une reponse accepted
- Fonctionner localement avec node local-test.mjs
- Supposer des evenements AWS Lambda HTTP API v2

Exigences:
- Expliquer index.mjs, events/create-order.json, local-test.mjs, index.test.mjs
- Retourner 400 si idempotency-key manque
- Retourner la meme reponse si idempotency-key est rejoue
- Distinguer invalid JSON, invalid input et unsupported route
- Logger en JSON sans secrets ni donnees personnelles
- Inclure une checklist avant deploiement

Contraintes:
- Aucun package npm externe
- En production, l'idempotence doit utiliser DynamoDB, KV ou un store durable
- IAM, URL publiques et ressources facturees exigent une confirmation humaine

Lambda Ou Workers ?

AWS Lambda est naturel pour les evenements AWS, IAM, S3, DynamoDB, SQS ou EventBridge. Cloudflare Workers est souvent meilleur pour l’HTTP edge : redirects, APIs legeres, cache, verification simple, KV/D1/R2. Vercel Functions est pratique dans une application Next.js, mais les exemples ci-dessous restent sur Lambda et Workers.

CritereAWS LambdaCloudflare Workers
Point fortIntegrations AWS, APIs metier, jobs asyncHTTP edge, routing, cache, APIs legeres
LocalNode.js, SAM, AWS CLIWrangler
PermissionsRole et policy IAMBindings, secrets, permissions compte
RisqueIAM trop large, cout VPC/NAT, volume logsDrift des bindings, limites runtime, KV consistency

Handler Lambda Executable Localement

// index.mjs
import crypto from "node:crypto";

const localIdempotencyStore = new Map();

function json(statusCode, body) {
  return {
    statusCode,
    headers: { "content-type": "application/json" },
    body: JSON.stringify(body),
  };
}

function readHeader(headers = {}, name) {
  const target = name.toLowerCase();
  const found = Object.entries(headers).find(([key]) => key.toLowerCase() === target);
  return found?.[1];
}

function parseBody(event) {
  if (!event.body) return {};
  const raw = event.isBase64Encoded
    ? Buffer.from(event.body, "base64").toString("utf8")
    : event.body;
  return JSON.parse(raw);
}

export async function handler(event = {}, context = {}) {
  const method = event.requestContext?.http?.method ?? event.httpMethod ?? "GET";
  const path = event.rawPath ?? event.path ?? "/";
  const requestId = context.awsRequestId ?? crypto.randomUUID();

  console.log(JSON.stringify({ level: "info", message: "request.start", requestId, method, path }));

  if (method !== "POST" || path !== "/orders") {
    return json(404, { error: "not_found" });
  }

  const idempotencyKey = readHeader(event.headers, "idempotency-key");
  if (!idempotencyKey) {
    return json(400, { error: "idempotency_key_required" });
  }

  if (localIdempotencyStore.has(idempotencyKey)) {
    return json(200, { ...localIdempotencyStore.get(idempotencyKey), replay: true });
  }

  let body;
  try {
    body = parseBody(event);
  } catch {
    return json(400, { error: "invalid_json" });
  }

  if (!Number.isFinite(body.amount) || body.amount <= 0 || typeof body.currency !== "string") {
    return json(400, { error: "invalid_order" });
  }

  const accepted = {
    orderId: crypto.randomUUID(),
    status: "accepted",
    amount: body.amount,
    currency: body.currency,
  };

  localIdempotencyStore.set(idempotencyKey, accepted);
  console.log(JSON.stringify({ level: "info", message: "order.accepted", requestId, orderId: accepted.orderId }));

  return json(202, accepted);
}
{
  "version": "2.0",
  "routeKey": "POST /orders",
  "rawPath": "/orders",
  "headers": {
    "content-type": "application/json",
    "idempotency-key": "demo-key-001"
  },
  "requestContext": {
    "http": {
      "method": "POST",
      "path": "/orders"
    }
  },
  "body": "{\"amount\":3200,\"currency\":\"EUR\"}",
  "isBase64Encoded": false
}
// local-test.mjs
import { readFile } from "node:fs/promises";
import { handler } from "./index.mjs";

const eventPath = process.argv[2] ?? "events/create-order.json";
const event = JSON.parse(await readFile(eventPath, "utf8"));

const first = await handler(event, { awsRequestId: "local-001" });
const second = await handler(event, { awsRequestId: "local-002" });

console.log("first:", first.statusCode, first.body);
console.log("second:", second.statusCode, second.body);
node local-test.mjs events/create-order.json

La Map n’est qu’une demo locale. En production, utilisez une ecriture conditionnelle DynamoDB, une contrainte unique en base, Cloudflare KV/D1 ou un autre stockage durable.

Tests

// index.test.mjs
import crypto from "node:crypto";
import test from "node:test";
import assert from "node:assert/strict";
import { handler } from "./index.mjs";

function event(overrides = {}) {
  return {
    rawPath: "/orders",
    headers: { "idempotency-key": crypto.randomUUID() },
    requestContext: { http: { method: "POST" } },
    body: JSON.stringify({ amount: 1200, currency: "EUR" }),
    isBase64Encoded: false,
    ...overrides,
  };
}

test("requires idempotency-key", async () => {
  const result = await handler(event({ headers: {} }), {});
  assert.equal(result.statusCode, 400);
});

test("accepts a valid order", async () => {
  const result = await handler(event(), {});
  assert.equal(result.statusCode, 202);
  assert.equal(JSON.parse(result.body).status, "accepted");
});

test("rejects invalid JSON", async () => {
  const result = await handler(event({ body: "not-json" }), {});
  assert.equal(result.statusCode, 400);
});
node --test index.test.mjs

Version Workers Avec KV

// src/worker.js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (request.method !== "POST" || url.pathname !== "/orders") {
      return Response.json({ error: "not_found" }, { status: 404 });
    }

    if (request.headers.get("x-webhook-secret") !== env.WEBHOOK_SECRET) {
      return Response.json({ error: "unauthorized" }, { status: 401 });
    }

    const idempotencyKey = request.headers.get("idempotency-key");
    if (!idempotencyKey) {
      return Response.json({ error: "idempotency_key_required" }, { status: 400 });
    }

    const existing = await env.IDEMPOTENCY_KV.get(idempotencyKey, "json");
    if (existing) {
      return Response.json({ ...existing, replay: true });
    }

    const body = await request.json();
    if (!Number.isFinite(body.amount) || typeof body.currency !== "string") {
      return Response.json({ error: "invalid_order" }, { status: 400 });
    }

    const accepted = {
      orderId: crypto.randomUUID(),
      status: "accepted",
      amount: body.amount,
      currency: body.currency,
    };

    await env.IDEMPOTENCY_KV.put(idempotencyKey, JSON.stringify(accepted), {
      expirationTtl: 86400,
    });

    return Response.json(accepted, { status: 202 });
  },
};
npm create cloudflare@latest serverless-orders-worker
cd serverless-orders-worker
npx wrangler kv namespace create IDEMPOTENCY_KV
npx wrangler secret put WEBHOOK_SECRET
npx wrangler dev

Pieges Et Checklist

Premier piege : croire a une execution exactement une fois. Les webhooks, queues, evenements async et navigateurs peuvent rejouer. Deuxieme piege : laisser des secrets dans les logs ou exemples. Troisieme piege : accepter des permissions trop larges comme Resource: "*". Quatrieme piege : publier une URL sans proprietaire, auth, CORS, rate limit, retention de logs et plan de suppression.

CheckA verifier
ExigencesInput, output, owner et erreurs sont documentes
RuntimeLambda Node.js runtime ou Workers compatibility_date est explicite
LocalFixture et node --test passent
Env/secretsConfig et secrets sont separes
IdempotenceUn retry ne cree pas de doublon
Timeout/retryLe travail lent va vers queue ou job durable
ObservabiliteLogs JSON, taux d’erreur, alertes et retention sont definis
CleanupCommandes de suppression ou etapes dashboard sont documentees
zip function.zip index.mjs
aws lambda update-function-code \
  --function-name serverless-orders-dev \
  --zip-file fileb://function.zip

npx wrangler deploy

Prompt final de revue :

Relis cette fonction serverless avant publication.
Separe blocking issues, non-blocking improvements et human confirmations.
Controle idempotence, timeout/retry, secrets, IAM ou bindings, logs,
reproductibilite locale, cleanup, liens officiels et liens internes.

ClaudeCodeLab rassemble ces pratiques dans des produits et templates Claude Code. Pour mettre en place permissions AWS, CLAUDE.md, prompts de revue et validation de deploiement dans une equipe, consultez la page formation et consultation Claude Code.

Dans l’essai pratique, le gain le plus net venait du fixture d’evenement cree en premier. Claude Code va vite, mais les retries, secrets et etapes de suppression deviennent fiables seulement si ces contraintes sont explicites des le premier prompt.

#Claude Code #serverless functions #AWS Lambda #Cloudflare Workers #Vercel
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.