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 Documentation、Lambda Node.js 文档、Cloudflare Workers development and testing 和 Workers 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 固定基础,再用 Gumroad 教材复用工作流;如果涉及团队导入、权限或收入路径,可以直接咨询。

Masa

关于作者

Masa

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