跳转到内容

Blog

从 AWS PDK 迁移

本指南将引导您完成将 AWS PDK 项目迁移至 Nx AWS 插件的示例,并提供相关主题的通用指导。

相较于 PDK,迁移至 Nx AWS 插件具有以下优势:

  • 更快的构建速度
  • 更易用的界面与 CLI
  • 支持氛围编程(试试我们的 MCP 服务器!
  • 更现代化的技术栈
  • 本地 API 与网站开发
  • 更高的控制权(可修改预置文件以适应需求)
  • 更多功能!

本指南将以 PDK 教程中的购物清单应用 作为迁移目标项目。若需跟随操作,请先按教程创建目标项目。

该购物清单应用包含以下 PDK 项目类型:

  • MonorepoTsProject
  • TypeSafeApiProject
  • CloudscapeReactTsWebsiteProject
  • InfrastructureTsProject

首先,为新建项目创建工作区。相较于原地迁移,此方法能获得最整洁的最终结果。创建 Nx 工作区相当于使用 PDK 的 MonorepoTsProject

Terminal window
npx create-nx-workspace@21.4.1 shopping-list --pm=pnpm --preset=@aws/nx-plugin@0.50.0 --iacProvider=CDK --ci=skip

在您常用的 IDE 中打开命令生成的 shopping-list 目录。

购物清单应用中的 TypeSafeApiProject 使用了:

  • Smithy 作为建模语言
  • TypeScript 实现操作逻辑
  • TypeScript 钩子生成器与 React 网站集成

因此我们可以使用 ts#smithy-api 生成器 来提供等效功能。

运行 ts#smithy-api 生成器packages/api 中创建 API 项目:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#smithy-api
  5. 填写必需参数
    • name: api
    • namespace: com.aws
    • auth: IAM
  6. 点击 Generate

这会生成 modelbackend 项目。model 包含 Smithy 模型,backend 包含服务端实现。后端使用 Smithy TypeScript 服务端生成器,下文将详细探讨。

建立基础结构后,按以下步骤迁移模型:

  1. 删除 packages/api/model/src 中的示例文件

  2. 将 PDK 项目 packages/api/model/src/main/smithy 的模型复制到新项目

  3. 更新 smithy-build.json 中的服务名称和命名空间:

    smithy-build.json
    "plugins": {
    "openapi": {
    "service": "com.aws#MyApi",
    ...
  4. main.smithy 中添加 ValidationException 错误:

    main.smithy
    use smithy.framework#ValidationException
    /// 我的购物清单 API
    @restJson1
    service MyApi {
    version: "1.0"
    operations: [
    GetShoppingLists
    PutShoppingList
    DeleteShoppingList
    ]
    errors: [
    BadRequestError
    NotAuthorizedError
    InternalFailureError
    ValidationException
    ]
    }
  5. packages/api/model/src 创建 extensions.smithy 定义分页特性:

    extensions.smithy
    $version: "2"
    namespace com.aws
    use smithy.openapi#specificationExtension
    @trait
    @specificationExtension(as: "x-cursor")
    structure cursor {
    inputToken: String
    enabled: Boolean
    }
  6. GetShoppingLists 操作添加 @cursor 特性:

    operations/get-shopping-lists.smithy
    @readonly
    @http(method: "GET", uri: "/shopping-list")
    @paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize", items: "shoppingLists")
    @cursor(inputToken: "nextToken")
    @handler(language: "typescript")
    operation GetShoppingLists {
    input := with [PaginatedInputMixin] {
    @httpQuery("shoppingListId")
    shoppingListId: ShoppingListId
    }
  7. 移除所有操作的 @handler 特性

运行构建检查模型变更:

Terminal window
pnpm nx run-many --target build

api/backend 项目相当于 Type Safe API 的 api/handlers/typescript。主要区别在于使用 Smithy 服务端生成器 而非 Type Safe API 的封装器。

安装依赖并迁移处理程序:

Terminal window
pnpm add -w @aws-sdk/client-dynamodb

迁移步骤:

  1. 复制处理程序文件到 backend/src/operations
  2. 替换运行时导入为 SSDK 生成类型
  3. 更新处理程序签名
  4. 使用 ctx.logger 替代拦截器
  5. 调整输入参数引用方式
  6. 返回普通对象替代 Response
  7. 更新导入为 ESM 格式
  8. service.ts 注册操作
点击查看完整前后对比

getApiServiceHandler 更新为 getMyApiServiceHandler

handler.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';
local-server.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';

更新项目元数据:

project.json
"metadata": {
"generator": "ts#smithy-api",
"apiName": "api",
"apiName": "my-api",
},

最后执行构建验证:

Terminal window
pnpm nx run-many --target build

购物清单应用中使用的CloudscapeReactTsWebsiteProject配置了一个内置CloudScape和Cognito身份验证的React网站。

该项目类型基于现已弃用的create-react-app。在本迁移指南中,我们将使用ts#react-website生成器,它采用了更现代且受支持的技术栈——Vite

作为迁移的一部分,我们还将从PDK配置的React Router迁移到TanStack Router,这将为网站路由增加额外的类型安全性。

运行ts#react-website生成器packages/website目录下创建网站项目:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#react-website
  5. 填写必需参数
    • name: website
  6. 点击 Generate

上述React网站生成器默认不包含Cognito身份验证(与CloudscapeReactTsWebsiteProject不同),需要通过ts#react-website#auth生成器显式添加:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#react-website#auth
  5. 填写必需参数
    • project: website
    • cognitoDomain: shopping-list
  6. 点击 Generate

这会添加管理重定向逻辑的React组件以确保用户通过Cognito托管UI登录,同时在packages/common/constructs目录下添加名为UserIdentity的CDK构造用于部署Cognito资源。

在PDK中,通过传递项目实例来触发集成代码生成。在Nx Plugin for AWS中,API集成通过api-connection生成器实现。运行以下命令使网站能够调用Smithy API:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - api-connection
  5. 填写必需参数
    • sourceProject: website
    • targetProject: api
  6. 点击 Generate

这将生成必要的客户端提供程序与构建目标,使网站可以通过生成的TypeScript客户端调用API。

由于CloudscapeReactTsWebsiteProject自动包含@aws-northstar/ui依赖,需手动添加:

Terminal window
pnpm add -w @aws-northstar/ui

购物清单应用包含CreateItem组件及ShoppingListShoppingLists两个页面。迁移时需调整以适应TanStack Router和Nx Plugin for AWS的TypeScript客户端代码生成。

  1. 将PDK项目中的packages/website/src/components/CreateItem/index.tsx复制到新项目相同位置

  2. packages/website/src/pages/ShoppingLists/index.tsx复制为packages/website/src/routes/index.tsx(因ShoppingLists是首页且使用文件路由)

  3. packages/website/src/pages/ShoppingList/index.tsx复制为packages/website/src/routes/$shoppingListId.tsx(对应/:shoppingListId路由)

迁移后IDE中可能出现构建错误,需进行后续调整。

使用文件路由时,可通过本地开发服务器自动生成路由配置。启动本地网站服务:

Terminal window
pnpm nx serve-local website

服务启动后(网站端口4200,Smithy API端口3001),按以下步骤迁移路由:

  1. 添加createFileRoute注册路由:

    import { createFileRoute } from "@tanstack/react-router";
    ...
    export default ShoppingLists;
    export const Route = createFileRoute('/')({
    component: ShoppingLists,
    });
  2. 替换useNavigate钩子:

    import { useNavigate } from 'react-router-dom';
    import { useNavigate } from '@tanstack/react-router';
    navigate(`/${cell.shoppingListId}`);
    navigate({
    to: '/$shoppingListId',
    params: { shoppingListId: cell.shoppingListId },
    });
  3. 替换useParams钩子:

    import { useParams } from 'react-router-dom';
    const { shoppingListId } = useParams();
    const { shoppingListId } = Route.useParams();

调整路由文件中的组件导入路径:

import CreateItem from "../../components/CreateItem";
import CreateItem from "../components/CreateItem";
import { AppLayoutContext } from "../../layouts/App";
import { AppLayoutContext } from "../components/AppLayout";

使用Nx Plugin for AWS生成的改进客户端:

  1. 替换旧客户端导入:

    import {
    ShoppingList,
    usePutShoppingList,
    useDeleteShoppingList,
    useGetShoppingLists,
    } from "myapi-typescript-react-query-hooks";
    import { ShoppingList } from "../generated/my-api/types.gen";
    import { useMyApi } from "../hooks/useMyApi";
    import { useInfiniteQuery, useMutation } from "@tanstack/react-query";
  2. 初始化新查询钩子:

    const getShoppingLists = useGetShoppingLists({ pageSize: PAGE_SIZE });
    const api = useMyApi();
    const getShoppingLists = useInfiniteQuery(
    api.getShoppingLists.infiniteQueryOptions(
    { pageSize: PAGE_SIZE },
    { getNextPageParam: (p) => p.nextToken },
    ),
    );
  3. 移除请求体包装器:

    await putShoppingList.mutateAsync({
    putShoppingListRequestContent: {
    name: item,
    },
    });

处理版本差异:

  1. 替换状态标识:

    putShoppingList.isLoading
    putShoppingList.isPending
  2. 类型断言兼容:

    <InfiniteQueryTable
    query={getShoppingLists}
    query={getShoppingLists as any}

完成迁移后,访问http://localhost:4200/即可查看网站。若本地存在`shopping_list`DynamoDB表且凭证有效,网站将完全可用。否则需继续迁移基础设施。

点击查看完整前后代码对比

我们购物清单应用需要迁移的最后一个项目是InfrastructureTsProject。这是一个TypeScript CDK项目,对应的Nx Plugin for AWS等效生成器是ts#infra生成器

与Projen项目类似,PDK还提供了这些项目所依赖的CDK构造。我们也将从这些CDK构造迁移购物清单应用,转而使用Nx Plugin for AWS生成的构造。

运行ts#infra生成器packages/infra目录下创建基础设施项目:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#infra
  5. 填写必需参数
    • name: infra
  6. 点击 Generate

PDK购物清单应用在CDK应用栈中实例化了以下构造:

  • 存储购物清单的DynamoDB表DatabaseConstruct
  • 从PDK直接导入Cognito资源的UserIdentity
  • 部署Smithy API的MyApi,使用生成的TypeScript CDK构造实现类型安全集成,底层依赖PDK的TypeSafeRestApi CDK构造
  • 部署网站的Website,封装了PDK的StaticWebsite CDK构造

接下来我们将逐一迁移这些构造。

将PDK购物清单应用中的packages/infra/src/stacks/application-stack.ts复制到新项目的相同位置。您会看到一些TypeScript错误,我们将在后续步骤中解决。

PDK购物清单应用在packages/src/constructs/database.ts中有一个Database构造。将其复制到新项目的相同位置。

由于Nx Plugin for AWS使用Checkov进行安全检查(比PDK Nag更严格),我们需要添加抑制规则:

constructs/database.ts
import { suppressRules } from ':shopping-list/common-constructs';
...
suppressRules(
this.shoppingListTable,
['CKV_AWS_28', 'CKV_AWS_119'],
'Backup and KMS key not required for this project',
);

application-stack.ts中更新DatabaseConstruct的导入语法为ESM格式:

stacks/application-stack.ts
import { DatabaseConstruct } from '../constructs/database';
import { DatabaseConstruct } from '../constructs/database.js';

UserIdentity构造通常只需调整导入路径即可直接替换:

import { UserIdentity } from "@aws/pdk/identity";
import { UserIdentity } from ':shopping-list/common-constructs';
...
const userIdentity = new UserIdentity(this, `${id}UserIdentity`);

注意新的UserIdentity构造底层直接使用aws-cdk-lib中的构造,而PDK使用的是@aws-cdk/aws-cognito-identitypool-alpha

PDK购物清单应用在constructs/apis/myapi.ts中有一个构造,用于实例化从Smithy模型生成的Type Safe API CDK构造。

由于PDK项目使用@handler特性,还生成了Lambda函数CDK构造。Nx Plugin for AWS通过更简洁灵活的方式实现类型安全集成——仅生成最小化的”元数据”,由packages/common/constructs/src/app/apis/api.ts通用处理。更多细节请参考ts#smithy-api生成器指南

迁移步骤如下:

  1. application-stack.ts中实例化Api构造

    stacks/application-stack.ts
    import { MyApi } from "../constructs/apis/myapi";
    import { Api } from ':shopping-list/common-constructs';
    ...
    const myapi = new MyApi(this, "MyApi", {
    databaseConstruct,
    userIdentity,
    });
    const api = new Api(this, 'MyApi', {
    integrations: Api.defaultIntegrations(this).build(),
    });

    这里使用Api.defaultIntegrations(this).build()默认会为API中的每个操作创建Lambda函数,与原有myapi.ts的行为一致。

  2. 授予Lambda函数访问DynamoDB表的权限

    在PDK应用中,DatabaseConstruct被传入MyApi并管理权限。现在直接在application-stack.ts中通过Api构造的integrations属性实现:

    stacks/application-stack.ts
    // 授予Lambda函数细粒度Dynamo访问权限
    databaseConstruct.shoppingListTable.grantReadData(
    api.integrations.getShoppingLists.handler,
    );
    [
    api.integrations.putShoppingList.handler,
    api.integrations.deleteShoppingList.handler,
    ].forEach((f) => databaseConstruct.shoppingListTable.grantWriteData(f));
  3. 授予认证用户调用API的权限

    在PDK的myapi.ts中,认证用户被授予API调用权限。现在在应用栈中实现等效操作:

    stacks/application-stack.ts
    api.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);

最后,将packages/common/constructs/src/app/static-websites/website.ts中的Website构造添加到application-stack.ts,替代原有PDK项目中的packages/infra/src/constructs/websites/website.ts

import { Website } from "../constructs/websites/website";
import { Website } from ':shopping-list/common-constructs';
...
new Website(this, "Website", {
userIdentity,
myapi,
});
new Website(this, 'Website');

注意不再传递身份或API参数——Nx Plugin for AWS的构造会自行管理运行时配置,UserIdentityApi注册必要值,Website负责部署到静态网站的/runtime-config.json

完成所有代码迁移后,现在可以构建项目:

Terminal window
pnpm nx run-many --target build

现在我们已经完成了代码库的完全迁移,接下来可以着手部署。此时有两种路径可供选择。

最简单的处理方式是将其视为一个全新的应用,即通过全新的 DynamoDB 表和 Cognito 用户池”重新开始”——但会丢失所有用户及其购物清单。采用此方案只需:

  1. 删除名为 shopping_list 的 DynamoDB 表

  2. 部署新应用:

    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*

🎉 大功告成!🎉

无中断迁移现有有状态资源(复杂方案)

Section titled “无中断迁移现有有状态资源(复杂方案)”

实际场景中,更可能需要将现有 AWS 资源迁移至新代码库管理,同时避免用户服务中断。

对于购物清单应用,需重点关注的有状态资源是存储用户购物清单的 DynamoDB 表和包含注册用户信息的 Cognito 用户池。我们的核心策略是保留这两个关键资源并将其迁移至新堆栈管理,然后更新 DNS 指向新网站(及面向客户的 API)。

  1. 更新新应用以引用需保留的现有资源

    针对购物清单应用的 DynamoDB 表:

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    针对 Cognito 用户池:

    packages/common/constructs/src/core/user-identity.ts
    this.userPool = this.createUserPool();
    this.userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );
  2. 构建并部署新应用:

    Terminal window
    pnpm nx run-many --target build
    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*

    此时新应用已部署完成,引用现有资源但尚未接收流量。

  3. 执行完整集成测试确保新应用功能正常。对于购物清单应用,需验证登录及购物清单的增删改查功能。

  4. 还原新应用中引用现有资源的修改(暂不部署):

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    针对 Cognito 用户池:

    packages/common/constructs/src/core/user-identity.ts
    this.userPool = this.createUserPool();
    this.userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );

    随后执行构建:

    Terminal window
    pnpm nx run-many --target build
  5. 在新应用的 packages/infra 目录执行 cdk import 查看需导入的资源:

    新应用
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    按回车逐步确认。由于资源仍由旧堆栈管理,导入会失败——这符合预期,此步骤仅用于确认需保留的资源。输出示例如下:

    Terminal window
    shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/smsRole/Resource (AWS::IAM::Role): 输入 RoleName(留空跳过)
    shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/Resource (AWS::Cognito::UserPool): 输入 UserPoolId(留空跳过)
    shopping-list-infra-sandbox/Application/Database/ShoppingList/Resource (AWS::DynamoDB::Table): 使用 TableName=shopping_list 导入 (y/n) y

    这表明实际需导入 3 个资源至新堆栈。

  6. 更新旧 PDK 项目,为上步发现的资源设置 RemovalPolicyRETAIN。当前 DynamoDB 表和用户池默认保留策略,但需为发现的 SMS 角色更新策略:

    application-stack.ts
    const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {
    userPool,
    });
    const smsRole = userIdentity.userPool.node.findAll().filter(
    c => CfnResource.isCfnResource(c) &&
    c.node.path.includes('/smsRole/'))[0] as CfnResource;
    smsRole.applyRemovalPolicy(RemovalPolicy.RETAIN);
  7. 部署 PDK 项目以应用保留策略:

    PDK 应用
    cd packages/infra
    npx projen deploy
  8. 查看 CloudFormation 控制台,记录 cdk import 步骤提示的信息:

    1. 用户池 ID,如 us-west-2_XXXXX
    2. SMS 角色名称,如 infra-sandbox-UserIdentityUserPoolsmsRoleXXXXXX
  9. 更新 PDK 项目以引用现有资源:

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    针对 Cognito 用户池:

    application-stack.ts
    const userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );
    const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {
    // PDK 构造接受 UserPool 而非 IUserPool,此处仍有效!
    userPool: userPool as any,
    });
  10. 再次部署 PDK 项目,使资源脱离原 CloudFormation 堆栈管理:

    PDK 应用
    cd packages/infra
    npx projen deploy
  11. 资源解除托管后,在新应用中执行 cdk import 完成实际导入:

    新应用
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    输入记录的值,导入应成功完成。

  12. 再次部署新应用以确保对现有资源(现由新堆栈管理)的变更生效:

    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*
  13. 再次全面测试新应用

  14. 更新 DNS 记录指向新网站(及 API 如需要)

    建议采用 Route53 加权路由逐步切换流量。初始阶段将部分请求导向新应用,通过监控指标逐步增加新应用权重,直至旧 PDK 应用无流量。

    若未配置 DNS 且使用自动生成域名,可通过 CloudFront HTTP 源站API Gateway HTTP 集成代理请求。

  15. 监控 PDK 应用指标确认无流量后,销毁旧 CloudFormation 堆栈:

    Terminal window
    cd packages/infra
    npx projen destroy

虽然过程复杂,但我们成功实现了用户的无缝迁移!🎉🎉🎉

现在,我们已获得 Nx Plugin for AWS 相较于 PDK 的新优势:

  • 更快的构建速度
  • 本地 API 开发支持
  • 更友好的编码体验(试试我们的 MCP 服务!
  • 更直观的类型安全客户端/服务端代码
  • 以及更多!

本节针对示例迁移未覆盖的 PDK 功能提供指导。

总体而言,从 PDK 迁移时建议从 Nx 工作区开始新项目(因其与 PDK Monorepo 相似),并推荐使用我们的生成器作为构建新类型的基础。

Terminal window
npx create-nx-workspace@21.4.1 my-project --pm=pnpm --preset=@aws/nx-plugin --ci=skip

CDK Graph 可构建连接CDK资源的图谱,并提供两个插件:

CDK Graph Diagram Plugin 能够根据CDK基础设施生成AWS架构图。

若需类似的确定性方法,CDK-Dia 是可行的替代方案。

随着生成式AI的进步,许多基础模型已能根据CDK基础设施生成高质量图表。我们推荐尝试 AWS Diagram MCP Server。通过这篇博客可获取详细操作指南。

CDK Graph Threat Composer Plugin 能够基于CDK代码生成初始的Threat Composer威胁模型。

该插件通过筛选包含示例威胁的基础威胁模型,并根据应用栈使用的资源进行过滤来运作。

若对这些示例威胁感兴趣,可复制并过滤基础威胁模型,或将其作为上下文辅助基础模型生成类似模型。

AWS Arch 为 CDK Graph 提供了 CloudFormation 资源与其对应架构图标的映射关系。

有关图标资源,请参考 AWS Architecture Icons 页面Diagrams 也提供了一种通过代码构建架构图的方式。

如果您需要直接使用这些资源,建议您 fork 该项目并自行维护!

PDK 提供了一个 PDKPipelineProject,它用于设置 CDK 基础设施项目,并利用了一个封装了部分 CDK Pipelines 资源的 CDK 构造体。

若需从此迁移,您可以直接使用 CDK Pipelines 的构造体。然而在实际操作中,采用如 GitHub Actions 或 GitLab CI/CD 这类工具可能更为简便。您只需定义 CDK Stages 并直接运行对应阶段的部署命令即可。

PDK Nag 封装了 CDK Nag,并提供了一套专门用于构建原型的安全规则

如需从 PDK Nag 迁移,请直接使用 CDK Nag。若需要相同的规则集,您可以通过此文档创建自己的”规则包”。

上述迁移示例涵盖了 Type Safe API 中最常用的组件,但其他功能的迁移细节如下所述。

Nx Plugin for AWS 支持使用 Smithy 建模的 API,但不支持直接使用 OpenAPI 建模的 API。ts#smithy-api 生成器是一个良好的起点,您可以在此基础上进行修改。您可以在model项目的src文件夹中定义 OpenAPI 规范而非 Smithy,并修改build.Dockerfile以使用您选择的代码生成工具来生成客户端/服务端代码(如果这些工具未在 NPM 上提供)。如果所需工具已在 NPM 上,您可以直接将其作为开发依赖安装到 Nx 工作区,并作为 Nx 构建目标直接调用。

对于使用 OpenAPI 建模的类型安全后端,可以考虑使用 OpenAPI Generator 服务端生成器。这些生成器不会直接生成适用于 AWS Lambda 的代码,但您可以使用 AWS Lambda Web Adapter 来弥合这一差距。

对于 TypeScript 客户端,可以使用 ts#react-website 生成器api-connection 生成器,结合示例 ts#smithy-api 来了解客户端的生成与网站集成方式。这会配置通过调用我们的 open-api#ts-clientopen-api#ts-hooks 生成器来生成客户端的构建目标。您可以通过指向 OpenAPI 规范来自行使用这些生成器。

对于其他语言,也可以查看 OpenAPI Generator 中的生成器是否符合需求。

您还可以使用 ts#nx-generator 生成器构建定制生成器。参考该生成器的文档了解如何从 OpenAPI 生成代码的细节。可以使用 Nx Plugin for AWS 的模板作为起点,甚至参考 PDK 代码库的模板获取更多灵感,注意模板操作的数据结构与 Nx Plugin for AWS 略有不同。

对于 TypeSpec,上述 OpenAPI 部分同样适用。您可以从生成 ts#smithy-api 开始,将 TypeSpec 编译器和 OpenAPI 包安装到 Nx 工作区,并更新模型项目的compile目标以运行tsp compile,确保其将 OpenAPI 规范输出到dist目录。

推荐方法是使用 TypeSpec HTTP Server generator for JavaScript 生成服务端代码,因为这直接作用于 TypeSpec 模型。

您可以使用 AWS Lambda Web Adapter 在 AWS Lambda 上运行生成的服务端。

也可以使用上述任何 OpenAPI 选项。

TypeSpec 为 Type Safe API 支持的三种语言提供了自己的客户端代码生成器:

由于 TypeSpec 可编译为 OpenAPI,上述 OpenAPI 部分同样适用。

上述迁移示例概述了如何迁移到使用 ts#smithy-api 生成器。本节涵盖 Python 和 Java 后端及客户端的选项。

Smithy 的 Java 代码生成器。该生成器包含 Java 服务端生成器以及适配器以在 AWS Lambda 上运行生成的 Java 服务端。

Smithy 没有 Python 的服务端生成器,因此需要通过 OpenAPI。请参考上述使用 OpenAPI 建模的 API 部分获取可能的选项。

Smithy 的 Java 代码生成器。该生成器包含 Java 客户端生成器。

对于 Python 客户端,可查看 Smithy Python

对于 TypeScript,查看 Smithy TypeScript,或采用与 ts#smithy-api 相同的通过 OpenAPI 的方法(我们选择此方法是为了通过 TanStack Query hooks 在 tRPC、FastAPI 和 Smithy API 之间保持一致性)。

Type Safe API 提供了一个名为 SmithyShapeLibraryProject 的 Projen 项目类型,用于配置包含可被多个基于 Smithy 的 API 复用的 Smithy 模型的项目。

最直接的实现方式如下:

  1. 使用 smithy#project 生成器创建模型库:

    1. 安装 Nx Console VSCode Plugin 如果您尚未安装
    2. 在VSCode中打开Nx控制台
    3. 点击 Generate (UI) 在"Common Nx Commands"部分
    4. 搜索 @aws/nx-plugin - smithy#project
    5. 填写必需参数
      • 点击 Generate

      serviceName 选项指定任意名称,因为我们将移除 service 模型。

    6. src 中的默认模型替换为您要定义的模型

    7. 更新 smithy-build.json 以移除 plugins 和未使用的 Maven 依赖

    8. build.Dockerfile 替换为最小化构建步骤:

      build.Dockerfile
      FROM public.ecr.aws/docker/library/node:24 AS builder
      # 输出目录
      RUN mkdir /out
      # 安装 Smithy CLI
      # https://smithy.io/2.0/guides/smithy-cli/cli_installation.html
      WORKDIR /smithy
      ARG TARGETPLATFORM
      RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH="aarch64"; else ARCH="x86_64"; fi && \
      mkdir -p smithy-install/smithy && \
      curl -L https://github.com/smithy-lang/smithy/releases/download/1.61.0/smithy-cli-linux-$ARCH.zip -o smithy-install/smithy-cli-linux-$ARCH.zip && \
      unzip -qo smithy-install/smithy-cli-linux-$ARCH.zip -d smithy-install && \
      mv smithy-install/smithy-cli-linux-$ARCH/* smithy-install/smithy
      RUN smithy-install/smithy/install
      # 复制项目文件
      COPY smithy-build.json .
      COPY src src
      # 使用 Maven 缓存挂载进行 Smithy 构建
      RUN --mount=type=cache,target=/root/.m2/repository,id=maven-cache \
      smithy build
      RUN cp -r build/* /out/
      # 导出 /out 目录
      FROM scratch AS export
      COPY --from=builder /out /

    在您的服务模型项目中进行以下更改以使用模型库:

    1. 更新 project.json 中的 compile 目标,添加工区作为构建上下文,并添加对模型库 build 目标的依赖

      project.json
      {
      "cache": true,
      "outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],
      "executor": "nx:run-commands",
      "options": {
      "commands": [
      "rimraf dist/packages/api/model/build",
      "make-dir dist/packages/api/model/build",
      "docker build --build-context workspace=. -f packages/api/model/build.Dockerfile --target export --output type=local,dest=dist/packages/api/model/build packages/api/model"
      ],
      "parallel": false,
      "cwd": "{workspaceRoot}"
      },
      "dependsOn": ["@my-project/shapes:build"]
      }
    2. 更新 build.Dockerfile 以从模型库复制 src 目录。例如,假设模型库位于 packages/shapes

      build.Dockerfile
      # 复制项目文件
      COPY smithy-build.json .
      COPY src src
      COPY --from=workspace packages/shapes/src shapes
    3. 更新 smithy-build.json 将模型目录添加到其 sources 中:

      smithy-build.json
      {
      "version": "1.0",
      "sources": ["src/", "shapes/"],
      "plugins": {
      ...
      }

    Type Safe API 提供了以下默认拦截器:

    • 使用 Powertools for AWS Lambda 的日志、跟踪和指标拦截器
    • 用于处理未捕获异常的 try-catch 拦截器
    • 用于返回 CORS 头部的 CORS 拦截器

    ts#smithy-api 生成器通过 Middy 使用 Powertools for AWS Lambda 实现日志、跟踪和指标。try-catch 拦截器的行为内置于 Smithy TypeScript SSDK 中,CORS 头部在 handler.ts 中添加。

    对于任何语言的日志、跟踪和指标拦截器,直接使用 Powertools for AWS Lambda

    对于迁移自定义拦截器,推荐使用以下库:

    Type Safe API 使用 Redocly CLI 生成文档。迁移后可以轻松添加到现有项目中。

    1. 安装 Redocly CLI

      Terminal window
      pnpm add -Dw @redocly/cli
    2. 使用 redocly build-docsmodel 项目添加文档生成目标,例如:

      model/project.json
      {
      ...
      "documentation": {
      "cache": true,
      "outputs": ["{workspaceRoot}/dist/{projectRoot}/documentation"],
      "executor": "nx:run-commands",
      "options": {
      "command": "redocly build-docs dist/packages/api/model/build/openapi/openapi.json --output=dist/packages/api/model/documentation/index.html",
      "cwd": "{workspaceRoot}"
      },
      "dependsOn": ["compile"]
      }
      }

    也可以考虑 OpenAPI Generator 文档生成器

    Type Safe API 在其生成的基础设施包中为您生成模拟。

    可以迁移到基于 JSON Schema 生成模拟数据的 JSON Schema Faker。这可以直接作用于 OpenAPI 规范,并具有CLI 工具,可作为 model 项目构建的一部分运行。

    可以更新 CDK 基础设施以读取 JSON Schema Faker 生成的 JSON 文件,并根据生成的 metadata.gen.ts(假设您使用了 ts#smithy-api 生成器)返回适当的 API Gateway MockIntegration

    Type Safe API 支持使用多种语言混合实现后端 API。这也可以通过在使用 CDK 实例化 API 构造时提供集成”覆盖”来实现:

    application-stack.ts
    const pythonLambdaHandler = new Function(this, 'PythonImplementation', {
    runtime: Runtime.PYTHON_3_12,
    ...
    });
    new MyApi(this, 'MyApi', {
    integrations: Api.defaultIntegrations(this)
    .withOverrides({
    echo: {
    integration: new LambdaIntegration(pythonLambdaHandler),
    handler: pythonLambdaHandler,
    },
    })
    .build(),
    });

    如果使用 ts#smithy-api 和 TypeScript 服务端 SDK,需要为服务”桩”化以通过编译,例如:

    service.ts
    export const Service: ApiService<ServiceContext> = {
    ...
    Echo: () => { throw new Error(`Not Implemented`); },
    };

    由于 Type Safe API 在底层使用了 SpecRestApi 构造,因此基于 OpenAPI 规范为请求体添加了原生 API Gateway 验证。

    使用 ts#smithy-api 生成器时,验证由服务端 SDK 自身执行。大多数服务端生成器也是如此。

    如果需要实现原生 API Gateway 验证,可以通过修改 packages/common/constructs/src/core/api/rest-api.ts 来从 OpenAPI 规范中读取每个操作请求体的相关 JSON schema。

    目前没有从 Type Safe API 使用 API Gateway 和 Lambda 的模型驱动 WebSocket API 的直接迁移路径。但本节旨在提供一些思路。

    考虑使用 AsyncAPI 代替 OpenAPI 或 TypeSpec 来建模 API,因为其专为异步 API 设计。AsyncAPI NodeJS 模板可以生成 Node WebSocket 后端,您可以在 ECS 上托管。

    也可以考虑将 AppSync Events 用于基础设施,并配合 Powertools这篇博客文章值得一读!

    另一个选项是在 AppSync 上使用带有 WebSocket 的 GraphQL API,我们有一个 GitHub issue 可供支持!参考 AppSync 开发者指南获取详情和示例项目链接。

    也可以考虑自行开发代码生成器来解释与 Type Safe API 相同的供应商扩展。参考使用 OpenAPI 建模的 API 部分了解构建自定义 OpenAPI 代码生成器的细节。可以在此处找到 Type Safe API 用于 API Gateway WebSocket API Lambda 处理程序的模板,客户端模板在此处

    也可以考虑迁移到使用 ts#trpc-api 生成器来使用 tRPC。截至撰写本文时,我们尚未支持订阅/流式传输,如需此功能请在GitHub issue 中添加 +1。

    Smithy 是协议无关的,但尚未支持 WebSocket 协议,参考此 GitHub issue

    当前版本的 Nx Plugin for AWS 暂不支持使用 Python 和 Java 编写的 PDK 兼容型 CDK 基础设施。

    建议的解决方案是:将您的 CDK 基础设施迁移至 TypeScript,或使用我们的生成器并将通用构造包迁移至目标语言。您可以使用生成式 AI 加速此类迁移,例如 Amazon Q CLI。通过 AI 代理进行迭代迁移,直至生成的 CloudFormation 模板完全一致。

    此原则同样适用于 Type Safe API 生成的 Python 或 Java 基础设施——您可以转换通用构造包中的 rest-api.ts 构造,并为目标语言实现简单的元数据生成器(参考 基于 OpenAPI 建模的 API 章节)。

    对于 Python 项目,您可以使用 py#project 生成器 作为基础项目来添加 CDK 代码(迁移 cdk.json 文件并添加相关构建目标)。Java 项目可使用 Nx 的 @nx/gradle 插件,Maven 项目可使用 @jnxplus/nx-maven

    PDK 构建于 Projen 之上。Projen 与 Nx Generators 存在根本性差异,这意味着虽然技术上可以结合使用,但这可能形成反模式。Projen 将项目文件作为代码管理,使其无法直接修改,而 Nx 生成器则一次性生成项目文件后,代码可自由修改。

    若希望继续使用 Projen,可自行实现所需的 Projen 项目类型。要遵循 AWS 的 Nx 插件模式,可运行我们的生成器或查阅 GitHub 上的源码,了解目标项目类型的构建方式,并利用 Projen 的原语实现相关部分。

    引入AWS MCP服务器的Nx插件

    在快速演进的软件开发领域中,AI助手已成为我们编码旅程中宝贵的协作伙伴。许多开发者已经欣然接受了我们亲切称为”氛围编码”(vibe-coding)的实践——这是人类创造力与AI辅助之间的协作之舞。如同任何新兴实践,它既带来了令人兴奋的优势,也伴随着显著的挑战。本文介绍的Nx Plugin for AWS MCP Server,将提升您在使用AWS产品和服务时的AI辅助开发体验。

    氛围编码——与AI助手协作构建软件的实践——已经改变了众多组织进行软件开发的方式。您描述想要构建的内容,AI助手则通过编写代码和测试、运行构建命令以及协作迭代来完成各种规模的任务,帮助您将愿景变为现实。

    这种协作方式显著加速了开发周期,以往需要手动编写数小时的复杂实现,现在通常可以在几分钟内完成。

    尽管优势显著,氛围编码仍存在可能中断工作流并导致挫败感的陷阱。AI工具可能在项目中产生不一致的模式,为后续维护埋下隐患。若缺乏具体指导,AI可能会忽略经验丰富的开发者通常会考虑的AWS特定最佳实践或安全因素。

    没有清晰的项目结构时,AI辅助生成的代码可能变得杂乱无章、难以维护。AI可能为已有成熟解决方案的问题创建自定义实现,造成不必要的重复劳动。

    这些挑战可能导致技术债务、安全漏洞和开发挫败感,特别是在处理各种互联的AWS服务时尤为明显,而不仅限于单一框架范围内。

    Nx Plugin for AWS为使用Nx单体仓库工具构建AWS应用提供了结构化基础。该插件通过为常见项目类型提供生成器,确保一致的项目脚手架,从而保持代码库的结构完整性。它采用遵循AWS最佳实践的预配置模板,帮助开发者规避常见陷阱和安全问题。集成工具提供内置命令用于构建、测试和部署AWS应用,并通过本地开发服务器简化开发流程。此外,该插件利用Nx强大的依赖管理能力处理复杂项目,简化单体仓库管理。

    通过提供这种结构,Nx Plugin for AWS为AI助手划定了明确的工作框架。AI助手可以遵循既定规范而非从头创建模式,从而生成更一致、更易维护的代码库。

    模型上下文协议(Model Context Protocol,MCP)是一个开放标准,允许AI助手与外部工具和资源交互。Nx Plugin for AWS MCP Server通过专门针对AWS开发的Nx插件知识扩展了AI助手的能力。

    MCP Server提供关于AWS开发最佳实践、可用项目结构和实现模式的上下文信息。它使AI工具能够创建工作区并运行生成器来搭建常见项目类型。这种上下文感知能力帮助AI做出更符合既定模式、规避常见陷阱的智能建议。

    通过MCP Server,您的AI助手可以为项目奠定坚实基础,避免生成不符合最佳实践或引用不存在功能的代码。最终带来更确定、更可靠的开发体验——您可以从核心组件的坚实基础开始,使用AI填充业务逻辑。

    如果您希望以更结构化、更可靠的方式探索AI辅助的AWS开发,请尝试Nx Plugin for AWS MCP Server。您可以通过以下MCP Server配置在常用AI助手(Amazon Q Developer、Cline、Claude Code等)中进行设置:

    {
    "mcpServers": {
    "aws-nx-mcp": {
    "command": "npx",
    "args": ["-y", "-p", "@aws/nx-plugin", "aws-nx-mcp"]
    }
    }
    }

    详细操作指南请参阅我们的AI辅助开发指南

    欢迎使用 @aws/nx-plugin

    直播开始啦!🚀

    Nx Plugin for AWS 是一款面向 Nx 的插件工具包,旨在简化全栈应用在 AWS 上的构建和部署流程。它为开发者提供了预配置的应用程序和基础设施即代码(IaC)模板,大幅减少了环境搭建与配置的时间。该插件在保持定制灵活性的同时,妥善处理了 AWS 服务集成的复杂性。

    用户只需从现有生成器列表中选择所需组件,配置必要选项,@aws/nx-plugin 便会自动生成对应的初始代码。工具包内包含多种生成器,可创建 API、网站、基础设施,甚至能通过 AST 转换实现前端与后端的深度集成(包括自动更新现有文件!)并生成类型安全的客户端。

    generator

    想深入了解?请通过我们的 地牢冒险教程 开始探索,该教程涵盖插件的核心组件,助您快速掌握使用方法。

    我们期待您的反馈!欢迎通过 讨论区 发表见解,或在 问题追踪 提出建议,告诉我们您希望看到哪些新功能!

    立即体验!