为什么我自己构建了 AWS 部署工具

发布: (2026年2月18日 GMT+8 01:40)
11 分钟阅读
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have the article, I’ll translate it into Simplified Chinese while preserving the original formatting, markdown, and technical terms.

我的快速无服务器部署工具之旅

我是一名拥有 12 年以上经验的软件工程师,涉猎多种语言、团队和数十个项目。我热爱我的工作,并且致力于用简洁的工具解决真实的问题。

问题

  • AWS 上的无服务器 – 我花了多年时间使用 Serverless FrameworkAWS CDK 构建无服务器应用。
  • 摩擦感 – 从有想法到在云上运行的过程比预期的要更繁琐。
  • CloudFormation 瓶颈 – 每一次部署都必须等待 CloudFormation 完成差异计算、计划和变更发布,即使只改动了一行代码。对于强调敏捷的无服务器场景,这种感觉就像拴着锚一样沉重。

来自其他云平台的启发

  • Google Cloud / Firebase – 我对 Firebase 的开发者体验印象深刻:在代码中描述函数,执行一次 CLI 命令,所有内容即被部署。
  • 为何 AWS 仍然更胜一筹 – AWS Lambda 更加简洁(无需容器构建,冷启动快),并且更符合我的思维模型,而不是 Google Cloud 那种“容器无处不在”的方式。

想法

“把 Firebase 的代码即配置模型与直接调用 AWS SDK 结合起来,完全跳过 CloudFormation。”

如果我能够编写 Lambda 处理函数,在代码旁边声明其配置,并通过一个命令部署全部——不需要 CloudFormation 模板、不需要堆栈定义、不需要打包工具配置、更不需要 IAM 样板代码——那么我就拥有了想要的快速反馈循环。

核心挑战

  1. 状态对齐 – 复制 CloudFormation 的行为:比较当前 AWS 状态与期望状态并同步差异,但仅使用直接的 SDK 调用。
  2. 类型安全的编排 – 在不让代码库失控的前提下,管理 API 调用、错误处理和并发。

让它成为可能的三大库

为什么有帮助
ts‑morph分析 TypeScript AST,直接从处理函数代码中提取基础设施信息(无需单独的 YAML/JSON)。
Effect‑TS提供函数式 effect 系统,用于编排 API 调用、处理错误以及在强类型安全下管理并发。
Typed AWS SDK wrapper(我自己写的)基于 AWS SDK 源码的 JSDoc,生成带 Effect 包装的 SDK 调用,并提供完整的类型化错误处理。

有了这些工具,我构建了 effortless‑aws

设计目标

  • 单对象配置 – 每个处理函数都是一次函数调用,接受一个选项对象(pathmethodhandler logicdependenciesparameters 等)。不使用柯里化函数或构建链。
  • 通过 Effect‑TS 实现上下文与依赖注入 – 处理函数在 Lambda 级别声明上下文工厂;该上下文在每个请求处理函数中注入,并在编译时保证所有依赖已满足。
  • 类型检查的依赖 – 需要其他资源(例如 DynamoDB 表)的处理函数只需添加 deps 字段,系统会自动完成 wiring。

最小化处理函数示例

import { defineHttp } from "effortless-aws";

export const hello = defineHttp({
  method: "GET",
  path: "/hello",
  onRequest: async ({ req }) => ({
    status: 200,
    body: { message: "Hello World!" },
  }),
});

这就是 Lambda 函数、API‑Gateway 路由以及 IAM 角色——全部写在同一个文件中。
运行 eff deploy 即可上线。

对比:CDK vs. Effortless‑AWS

CDK – 创建一个带 DynamoDB 表的简单 HTTP 端点

// CDK: stack definition (separate file)
const table = new dynamodb.Table(this, "Orders", {
  partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

const fn = new nodejs.NodejsFunction(this, "GetOrders", {
  entry: "src/handlers/get-orders.ts",
  runtime: lambda.Runtime.NODEJS_20_X,
  environment: { TABLE_NAME: table.tableName },
  bundling: { /* … */ },
});
  • 多文件 – 栈定义、函数定义和打包配置各自分离。
  • CloudFormation – 部署需要等待 diff/plan/execute 循环。
  • 样板代码 – IAM 角色、权限和资源引用必须手动连接。

Effortless‑AWS – 同样的结果,一处完成

import { defineHttp } from "effortless-aws";

export const getOrders = defineHttp({
  method: "GET",
  path: "/orders",
  deps: {
    table: {
      name: "Orders",
      partitionKey: "id",
      billingMode: "PAY_PER_REQUEST",
    },
  },
  onRequest: async ({ deps }) => {
    const items = await deps.table.scan().items();
    return { status: 200, body: items };
  },
});
  • 单文件 – 处理器、API 路由、DynamoDB 表和 IAM 权限一起声明。
  • 无需 CloudFormation – 直接调用 SDK,使部署几乎瞬时完成。
  • 零样板代码 – 框架会根据 deps 对象自动推断 IAM 策略和关联。

要点

  • 对于无服务器工作负载跳过 CloudFormation 可以把反馈循环缩短几分钟(甚至几秒)。
  • 代码即配置 的方法,借助 TypeScript AST 分析和强大的 effect 系统,让你在 AWS 上获得类似 Firebase 的开发体验。
  • 通过恰当的类型层级保证,你可以在保持原始 SDK 调用灵活性的同时,无需 手动 wiring 的混乱。

这就是我最终忍不住自己打造部署工具 effortless‑aws 的原因,以及它如何解决我在使用 Serverless Framework、CDK 和 CloudFormation 时遇到的痛点。

使用 CDK 的基础设施(用于比较)

fy: true, sourceMap: true },
});

table.grantReadData(fn);

const api = new apigateway.HttpApi(this, "Api");
api.addRoutes({
  path: "/orders",
  methods: [apigateway.HttpMethod.GET],
  integration: new HttpLambdaIntegration("GetOrdersIntegration", fn),
});

这仅仅是基础设施。你仍然需要处理程序文件、堆栈配置、应用入口点,以及使用 CloudFormation 的 cdk deploy

使用 effortless‑aws 同样的做法

“一个文件。一个命令。”

// That's it. One file.
import { defineHttp, defineTable } from "effortless-aws";

export const orders = defineTable({
  pk: "id",
});

export const getOrders = defineHttp({
  method: "GET",
  path: "/orders",
  deps: { orders },
  onRequest: async ({ deps }) => {
    const items = await deps.orders.scan();
    return { status: 200, body: items };
  },
});

运行:

eff deploy

完成。表、函数、路由、IAM 权限——全部在几秒钟内创建完毕。

eff deploy 的工作原理

eff deploy 经过 四个阶段。协调这些阶段是项目中最难的部分——每个阶段都会向下一个阶段传递信息,错误可能在任何地方出现,且资源之间相互依赖。Effect‑TS 让这变得可控:整个流水线都是可组合的,每一步都是一个可以独立推理的 Effect

1. 代码分析(ts‑morph)

  • ts‑morph 读取你的 TypeScript 源码,并直接从 AST 中提取每个 defineHttp / defineTable 调用——方法、路径、依赖、参数、静态文件等。
  • 这取代了单独的 YAML/JSON 配置文件;代码本身成为唯一的真相来源。

2. 打包(esbuild)

  • esbuild 将每个处理函数编译成一个单独的 ESM 文件。
  • 为了避免把相同的 node_modules 打包进每个 Lambda ZIP,公共依赖会放入 Lambda Layer(整个项目共用一个层)。
  • 每个处理函数最终生成一个干净的单文件 JS;运行时通过层提供 node_modules
  • 目前已在 pnpm 项目中可靠运行。

3. 状态对比

  • 工具检查 AWS 中已经存在的资源,并将其与代码声明的资源进行比较。
  • 只对差异进行应用——本质上类似 CloudFormation 的行为,但不需要额外的 provisioning‑engine 开销。
  • 最大的概念挑战是找到合适的对比粒度并确保更新的幂等性。

4. 资源供应(AWS SDK + Effect‑TS)

  • 直接调用 AWS SDK 创建、更新或重新配置资源(Lambda 函数、DynamoDB 表、API Gateway 路由、IAM 策略)。
  • 每一次 AWS 调用都经过类型化的 Effect 包装,因此所有可能的错误都是已知的。
    • 如果函数已存在 → 记录日志并继续。
    • 如果出现内部 AWS 错误 → 停止部署。

项目状态

  • effortless‑aws 当前处于 alpha 阶段。
  • 已经在生产环境中使用于:
    • 文档网站。
    • 使用 Lambda 函数、DynamoDB 表、流和触发器的个人项目。
  • 所有内容均可部署并运行,但仍有一些粗糙之处,且正在积极开发中。

路线图

  • 新的处理程序(例如 S3 静态网站、EventBridge)。
  • 改进的 diff 算法。
  • 更好的 TypeScript 使用体验。

安装与资源

npm install effortless-aws
  • GitHub:
  • 网站与文档:

用于构建项目的工具

工具目的
Effect‑TS为 TypeScript 提供类型化函数式编程
ts‑morphTypeScript AST 分析与操作
esbuild快速的 JavaScript/TypeScript 打包工具
AWS SDK for JavaScript (v3)官方 AWS SDK
Typed AWS SDK wrapper为 AWS SDK 提供带类型错误的 Effect 包装器
0 浏览
Back to Blog

相关文章

阅读更多 »