Use Cases (更新: 2026/6/1)

用 Claude Code 构建 Serverless Functions:Lambda 与 Workers 实战

用 Claude Code 构建可靠的 serverless functions:需求提示词、平台选择、环境变量、幂等、重试、测试和部署检查。

用 Claude Code 构建 Serverless Functions:Lambda 与 Workers 实战

Serverless functions 指的是:不需要长期管理服务器,而是在事件或 HTTP 请求到来时短时间执行代码的机制。它适合 Webhook、小型 API、图片或 CSV 处理入口、边缘重定向等场景。但它不是“没有运维”的魔法;超时、重试、密钥、权限、日志和费用都需要提前设计。

Claude Code 的价值在于把处理函数、事件样例、测试、部署说明和审查清单放在同一个上下文里。安全的做法不是让 Claude Code 直接部署,而是先写清需求,再选择运行时和平台,本地复现事件,分离配置与密钥,设计幂等性,测试失败路径,最后再让人审查公开范围和成本。

建议同时打开官方文档:AWS Lambda DocumentationLambda Node.js 文档Cloudflare Workers development and testingWorkers get started guide。站内延伸阅读包括 AWS Lambda 指南Cloudflare Workers 指南API 开发指南密钥管理指南

先确定适用场景

Serverless functions 最适合短时间、事件驱动、可以安全重试的工作。

场景为什么适合可让 Claude Code 起草人必须确认
支付或表单 Webhook一个请求对应一个事件签名校验、事件 fixture、错误响应密钥、重复通知、重放规则
图片压缩或 CSV 导入入口重任务可交给存储或队列输入校验、job ID、结构化日志文件大小、超时、失败清理
内部 JSON API小端点不需要常驻服务器handler、测试、路由认证、CORS、公开范围、限流
边缘跳转和缓存可在离用户更近的位置响应Worker 路由、缓存头、发布说明缓存清理、个人信息、SEO 影响
flowchart LR
  A[写需求提示词] --> B[选择 Lambda 或 Workers]
  B --> C[本地复现事件]
  C --> D[分离环境变量和密钥]
  D --> E[设计幂等与重试]
  E --> F[运行测试]
  F --> G[部署到 dev]
  G --> H[检查日志和清理路径]

给 Claude Code 的需求提示词

不要只说“做一个 serverless API”。要给它输入、输出、失败方式、可编辑文件和人工确认点。

请用 Node.js 创建一个最小 serverless function。

目标:
- 处理 POST /orders,并返回订单已接收的响应
- 可以通过 node local-test.mjs 在本地运行
- 事件格式按 AWS Lambda HTTP API v2 处理

要求:
- 说明 index.mjs、events/create-order.json、local-test.mjs、index.test.mjs
- 缺少 idempotency-key 时返回 400
- 同一个 idempotency-key 重复请求时返回相同结果
- 区分 invalid JSON、invalid input、unsupported route
- 日志用 JSON,但不能输出密钥或个人信息
- 给出部署前检查清单

限制:
- 不使用外部 npm 包
- 生产幂等存储应替换为 DynamoDB、KV 或其他持久存储
- IAM、公开 URL 和会产生费用的资源必须人工确认

平台选择

如果函数需要 AWS 事件、IAM、S3、DynamoDB、SQS 或 EventBridge,优先考虑 AWS Lambda。如果核心是边缘 HTTP 处理、轻量鉴权、重定向、缓存策略或 KV/D1/R2 组合,Cloudflare Workers 更自然。Vercel Functions 适合和 Next.js 应用一起发布的 API 或 OG 图片生成,但本文示例聚焦在 Lambda 与 Workers。

判断点AWS LambdaCloudflare Workers
适合任务AWS 集成、业务 API、异步任务边缘 HTTP、路由、缓存、轻量 API
本地开发Node.js、SAM、AWS CLIWrangler
权限IAM role 和 policybinding、secret、账号权限
常见风险IAM 过宽、VPC/NAT 成本、日志量binding 不一致、运行限制、KV 一致性

可本地运行的 Lambda 示例

先不要上 AWS。下面的 handler 没有外部依赖,能在本地验证 HTTP API 事件、idempotency-key 和重复请求行为。

// 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\":\"CNY\"}",
  "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

这里的 Map 只适合本地演示。生产环境必须使用 DynamoDB 条件写入、数据库唯一键、Cloudflare KV/D1 或其他持久存储,因为 Lambda 的内存不能作为真实状态来源。

测试失败路径

// 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: "CNY" }),
    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

Workers 版本

Workers 的入口是 fetch(request, env)。下面的示例把幂等结果放到 KV,把 Webhook 密钥放到 Worker secret。

// 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 });
  },
};
{
  "name": "serverless-orders-worker",
  "main": "src/worker.js",
  "compatibility_date": "2026-06-01",
  "kv_namespaces": [
    {
      "binding": "IDEMPOTENCY_KV",
      "id": "replace_with_real_kv_namespace_id"
    }
  ]
}
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

常见陷阱和部署清单

最大的陷阱是以为函数只会执行一次。Webhook 提供方、队列、异步 Lambda 事件和浏览器都可能重试。第二个陷阱是把密钥写进环境变量后又输出到日志。第三个陷阱是权限过宽,例如 Resource: "*"。第四个陷阱是公开了 API Gateway 或 Worker 路由,却没有认证、CORS、限流、日志保留和删除计划。

部署前至少确认这些项目:

检查项内容
需求输入、输出、负责人、失败响应已经写明
运行时Lambda Node.js runtime 或 Workers compatibility date 明确
本地证明fixture 和 node --test 通过
环境与密钥配置和密钥分离,日志不输出真实值
幂等性重试不会重复收费或重复创建
超时/重试慢任务进入队列或持久 job
可观测性JSON 日志、错误率、告警、日志保留已定义
清理删除命令或控制台清理步骤已记录
zip function.zip index.mjs
aws lambda update-function-code \
  --function-name serverless-orders-dev \
  --zip-file fileb://function.zip

npx wrangler deploy

最后让 Claude Code 做发布前审查:

请审查这个 serverless function。
按 blocking、non-blocking、human confirmation 分类。
检查幂等性、timeout/retry、密钥泄露、IAM 或 binding 过宽、日志个人信息、
本地测试复现性、清理步骤、官方链接和内部链接。

ClaudeCodeLab 将这类流程整理成 Claude Code 模板和产品。如果团队需要围绕 AWS 权限、CLAUDE.md、审查提示词和部署审批建立流程,可以查看 Claude Code 咨询与培训

实际试用后,收益最大的是先创建事件 fixture。Claude Code 很快,但只有在提示词里明确重试、密钥、日志和清理要求时,它才会生成可审查的 serverless function。

#Claude Code #serverless functions #AWS Lambda #Cloudflare Workers #Vercel
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。