Use Cases (Actualizado: 1/6/2026)

Serverless Functions con Claude Code: Lambda y Workers

Guia practica para crear serverless functions con Claude Code: prompt, plataforma, env/secrets, idempotencia, retries, tests y deploy.

Serverless Functions con Claude Code: Lambda y Workers

Las serverless functions son piezas pequenas de codigo que se ejecutan por cada evento o solicitud HTTP, sin mantener un servidor siempre encendido. Funcionan muy bien para webhooks, APIs pequenas, entradas de procesamiento de imagenes o CSV, y reglas edge. Pero siguen exigiendo decisiones serias sobre timeout, retry, secrets, permisos, logs y coste.

Claude Code ayuda porque puede mantener en el mismo contexto el handler, el fixture del evento, los tests, las notas de despliegue y la checklist de revision. El flujo seguro no es dejar que despliegue solo. El flujo seguro es: escribir requisitos, elegir runtime y plataforma, reproducir el evento en local, separar configuracion y secrets, disenar idempotencia, probar fallos y pedir revision humana para exposicion y coste.

Usa la documentacion oficial como referencia: AWS Lambda Documentation, Lambda con Node.js, Cloudflare Workers development and testing y Workers get started guide. Para profundizar, revisa la guia AWS Lambda, la guia Cloudflare Workers, la guia de desarrollo de APIs y la guia de gestion de secrets.

Primero El Caso De Uso

Serverless funciona mejor cuando el trabajo es corto, esta ligado a eventos y puede repetirse sin romper datos.

Caso de usoPor que encajaClaude Code puede redactarRevision humana
Webhook de pagos o formulariosUna request se convierte en un eventoVerificacion de firma, fixture, respuestas de errorSecrets, duplicados, replay
Entrada de resize o CSVEl trabajo pesado va a storage o queueValidacion, job ID, logs JSONTamano de archivo, timeout, cleanup
API JSON internaNo hace falta servidor persistenteHandler, tests, routeAuth, CORS, exposicion, rate limit
Redirect/cache edgeRespuesta cerca del usuarioWorker route, headers cache, rolloutPurga cache, datos personales, SEO
flowchart LR
  A[Prompt de requisitos] --> B[Elegir Lambda o Workers]
  B --> C[Reproducir evento local]
  C --> D[Separar env y secrets]
  D --> E[Idempotencia y retry]
  E --> F[Tests]
  F --> G[Deploy a dev]
  G --> H[Logs y cleanup]

Prompt Para Claude Code

Crea una funcion serverless minima en Node.js.

Objetivo:
- Manejar POST /orders y devolver una respuesta accepted
- Ejecutarse localmente con node local-test.mjs
- Asumir eventos AWS Lambda HTTP API v2

Requisitos:
- Explicar index.mjs, events/create-order.json, local-test.mjs e index.test.mjs
- Devolver 400 si falta idempotency-key
- Devolver la misma respuesta si se repite idempotency-key
- Separar invalid JSON, invalid input y unsupported route
- Logs en JSON sin secrets ni datos personales
- Incluir checklist antes de deploy

Restricciones:
- Sin paquetes npm externos
- En produccion, la idempotencia debe usar DynamoDB, KV u otro storage durable
- IAM, URLs publicas y recursos facturables requieren confirmacion humana

Lambda O Workers

AWS Lambda encaja cuando necesitas eventos AWS, IAM, S3, DynamoDB, SQS o EventBridge. Cloudflare Workers encaja cuando el trabajo principal es HTTP en el edge: redirects, APIs ligeras, cache, validacion simple, KV/D1/R2. Vercel Functions es util dentro de Next.js, pero aqui usamos Lambda y Workers para mantener conceptos verificables.

CriterioAWS LambdaCloudflare Workers
Mejor paraIntegraciones AWS, APIs de negocio, jobs asyncHTTP edge, routing, cache, APIs ligeras
LocalNode.js, SAM, AWS CLIWrangler
PermisosIAM role y policyBindings, secrets, permisos de cuenta
Riesgo comunIAM amplio, coste VPC/NAT, volumen de logsDrift de bindings, limites runtime, KV consistency

Handler Lambda Ejecutable

// 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 solo sirve para demo local. En produccion, usa escrituras condicionales en DynamoDB, una restriccion unica de base de datos, Cloudflare KV/D1 u otro almacenamiento 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 Con 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

Errores Frecuentes Y Checklist

El primer error es asumir ejecucion exactamente una vez. Webhooks, colas, eventos async y navegadores pueden reintentar. El segundo es filtrar secrets en logs o ejemplos. El tercero es aceptar permisos amplios como Resource: "*". El cuarto es publicar una URL sin owner, auth, CORS, rate limit, retencion de logs y plan de borrado.

CheckQue confirmar
RequisitosInput, output, owner y errores documentados
RuntimeLambda Node.js runtime o Workers compatibility_date explicito
LocalFixture y node --test pasan
Env/secretsConfiguracion y secrets separados
IdempotenciaRetry no duplica cobros ni registros
Timeout/retryTrabajo lento va a queue o job durable
ObservabilidadLogs JSON, error rate, alertas y retention definidos
CleanupComandos de borrado o pasos de dashboard escritos
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:

Revisa esta serverless function antes de publicar.
Separa blocking issues, non-blocking improvements y human confirmations.
Comprueba idempotencia, timeout/retry, secrets, IAM o bindings, logs,
reproducibilidad local, cleanup, enlaces oficiales y enlaces internos.

ClaudeCodeLab empaqueta estos patrones en productos y templates de Claude Code. Para disenar permisos AWS, CLAUDE.md, prompts de revision y aprobaciones de deploy en un repositorio real, revisa la pagina de consultoria y formacion Claude Code.

En la prueba practica, el mayor beneficio fue crear primero el fixture de evento. Claude Code es rapido, pero solo maneja bien retries, secrets y cleanup cuando esas restricciones estan en el primer prompt.

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

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.