跳转到内容

Jack

3 posts by Jack

从 AWS PDK 迁移

本指南将通过一个 AWS PDK 项目迁移到 Nx Plugin for AWS 的示例,为您提供有关此主题的一般指导。

迁移到 Nx Plugin for AWS 相比 PDK 具有以下优势:

  • 更快的构建速度
  • 更易于使用(UI 和 CLI)
  • 适合 Vibe-coding(试试我们的 MCP 服务器!
  • 更现代的技术
  • 本地 API 和网站开发
  • 更多控制权(修改生成的文件以适应您的用例)
  • 还有更多!

在本指南中,我们将使用 PDK 教程中的购物清单应用程序作为我们要迁移的目标项目。如果您希望自己跟随操作,请按照该教程中的步骤创建目标项目。

购物清单应用程序由以下 PDK 项目类型组成:

  • MonorepoTsProject
  • TypeSafeApiProject
  • CloudscapeReactTsWebsiteProject
  • InfrastructureTsProject

首先,我们将为新项目创建一个新的工作区。虽然这比就地迁移更极端,但这种方法为我们提供了最干净的最终结果。创建 Nx 工作区相当于使用 PDK 的 MonorepoTsProject

Terminal window
pnpm create @aws/nx-workspace@1.0.0-rc.47 shopping-list --iac=cdk

在您喜欢的 IDE 中打开此命令创建的 shopping-list 目录。

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

  • Smithy 作为建模语言
  • TypeScript 用于实现操作
  • TypeScript 钩子生成用于与 react 网站集成

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

运行 ts#api 生成器,将 framework 设置为 smithy,以在 packages/api 中设置您的 api 项目:

Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=api --framework=smithy --namespace=com.aws --auth=iam --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=api --framework=smithy --namespace=com.aws --auth=iam --no-interactive --dry-run

您会注意到这会生成一个 model 项目以及一个 backend 项目。model 项目包含您的 Smithy 模型,backend 包含您的服务器实现。

后端使用 Smithy Server Generator for TypeScript。我们将在下面进一步探讨这一点。

现在我们有了 Smithy API 项目的基本结构,我们可以迁移模型:

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

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

  3. 更新 smithy-build.json 中的服务名称和命名空间以匹配 PDK 应用程序:

    smithy-build.json
    "plugins": {
    "openapi": {
    "service": "com.aws#MyApi",
    ...
  4. 更新 main.smithy 中的服务以添加 ValidationException 错误,这在使用 Smithy TypeScript Server SDK 时是必需的。

    main.smithy
    use smithy.framework#ValidationException
    /// My Shopping List 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. get-shopping-lists.smithy 中将新的 @cursor 特征添加到 GetShoppingLists 操作:

    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
    }

    如果您使用 Nx Plugin for AWS 提供的客户端生成器(通过 api-connection 生成器),任何 @paginated 操作也应该使用 @cursor

  7. 最后,从所有操作中删除 @handler 特征,因为 Nx Plugin for AWS 不支持此特征。使用 ts#smithy-api,我们不需要此特征生成的自动生成的 lambda 函数 CDK 构造和打包目标,因为我们对所有 lambda 函数使用单个包。

此时,让我们运行构建以检查我们的模型更改并确保我们有一些生成的服务器代码可以使用。后端项目(@shopping-list/api)中会有一些失败,但我们接下来会解决这些问题。

Terminal window
pnpm nx run-many --target build

您可以将 api/backend 项目视为与 Type Safe API 的 api/handlers/typescript 项目有些等效。

Type Safe API 和 ts#smithy-api 生成器之间的主要区别之一是处理程序使用 Smithy Server Generator for TypeScript 实现,而不是 Type Safe API 自己生成的处理程序包装器(在 api/generated/typescript/runtime 项目中找到)。

购物清单应用程序的 lambda 处理程序依赖于 @aws-sdk/client-dynamodb 包,因此让我们将其安装到 @shopping-list/api 项目中:

Terminal window
pnpm add @aws-sdk/client-dynamodb --filter api

然后,让我们将 PDK 项目中的 handlers/src/dynamo-client.ts 文件复制到 backend/src/operations,以便我们的处理程序可以使用它。

ts#smithy-api 生成器搭建了一个示例 Echo 操作。由于我们从模型中删除了它,请删除 backend/src/operations/echo.ts 中的相应处理程序。我们将在下面的 service.ts 中注册我们迁移的操作。

要迁移处理程序,您可以遵循以下一般步骤:

  1. 将处理程序从 PDK 项目的 packages/api/handlers/typescript/src 目录复制到新项目的 packages/api/backend/src/operations 目录。

  2. 删除 my-api-typescript-runtime 导入,而是从生成的 TypeScript Server SDK 导入操作类型,以及 ServiceContext,例如:

    import {
    deleteShoppingListHandler,
    DeleteShoppingListChainedHandlerFunction,
    INTERCEPTORS,
    Response,
    LoggingInterceptor,
    } from 'myapi-typescript-runtime';
    import { DeleteShoppingList as DeleteShoppingListOperation } from '../generated/ssdk/index.js';
    import { ServiceContext } from '../context.js';
  3. 删除处理程序包装器导出

    export const handler = deleteShoppingListHandler(
    ...INTERCEPTORS,
    deleteShoppingList,
    );
  4. 更新操作处理程序的签名以使用 SSDK:

    export const deleteShoppingList: DeleteShoppingListChainedHandlerFunction = async (request) => {
    export const DeleteShoppingList: DeleteShoppingListOperation<ServiceContext> = async (input, ctx) => {
  5. LoggingInterceptor 的使用替换为 ctx.logger。(也适用于指标和跟踪拦截器):

    LoggingInterceptor.getLogger(request).info('...');
    ctx.logger.info('...');
  6. 更新对输入参数的引用。由于 SSDK 提供的类型与您的 Smithy 模型完全匹配(而不是将路径/查询/标头参数与正文参数分开分组),请相应地更新任何输入引用:

    const shoppingListId = request.input.requestParameters.shoppingListId;
    const shoppingListId = input.shoppingListId;
  7. 删除 Response 的使用。我们在 SSDK 中只返回普通对象。

    return Response.success({ shoppingListId });
    return { shoppingListId };

    我们也不再抛出或返回 Response,而是抛出 SSDK 生成的错误:

    throw Response.badRequest({ message: 'oh no' });
    return Response.badRequest({ message: 'oh no' });
    import { BadRequestError } from '../generated/ssdk/index.js';
    throw new BadRequestError({ message: 'oh no' });
  8. 更新任何导入以使用 ESM 语法,即向相对导入添加 .js 扩展名。

  9. 将操作添加到 service.ts

    service.ts
    import { ServiceContext } from './context.js';
    import { MyApiService } from './generated/ssdk/index.js';
    import { DeleteShoppingList } from './operations/delete-shopping-list.js';
    import { GetShoppingLists } from './operations/get-shopping-lists.js';
    import { PutShoppingList } from './operations/put-shopping-list.js';
    // Register operations to the service here
    export const Service: MyApiService<ServiceContext> = {
    PutShoppingList,
    GetShoppingLists,
    DeleteShoppingList,
    };
点击此处查看教程中三个购物清单操作的完整前后示例

我们最初使用名称 api 生成了 Smithy API 项目,因为我们希望将其添加到 packages/api 以与 PDK 项目保持一致。由于我们的 Smithy API 现在定义了 service MyApi 而不是 service Api,我们需要将 getApiServiceHandler 的所有实例更新为 getMyApiServiceHandler

handler.ts 进行此更改:

packages/api/backend/src/handler.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';
process.env.POWERTOOLS_METRICS_NAMESPACE = 'Api';
process.env.POWERTOOLS_SERVICE_NAME = 'Api';
const tracer = new Tracer();
const logger = new Logger();
const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service);
const serviceHandler = getMyApiServiceHandler(Service);

以及对 local-server.ts

packages/api/backend/src/local-server.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';
const PORT = 3001;
const tracer = new Tracer();
const logger = new Logger();
const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service);
const serviceHandler = getMyApiServiceHandler(Service);

此外,更新 packages/api/backend/project.json 并将 metadata.apiName 更新为 my-api

packages/api/backend/project.json
"metadata": {
"generator": "ts#smithy-api",
"apiName": "api",
"apiName": "my-api",
"auth": "iam",
"modelProject": "@shopping-list/api-model",
"ports": [3001]
},

我们现在可以构建项目以检查迁移到目前为止是否有效:

Terminal window
pnpm nx run-many --target build

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

此项目类型利用了 create-react-app,该工具现已弃用。在本指南中迁移网站时,我们将使用 ts#website 生成器,它使用更现代且受支持的技术,即 Vite

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

运行 ts#website 生成器,将 framework 设置为 react,以在 packages/website 中设置您的网站项目。由于购物清单应用程序是使用 CloudScape 组件构建的,我们还将 ux 设置为 cloudscape(默认值为 shadcn):

Terminal window
pnpm nx g @aws/nx-plugin:ts#website --name=website --framework=react --ux=cloudscape --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#website --name=website --framework=react --ux=cloudscape --no-interactive --dry-run

上面的 React 网站生成器默认不像 CloudscapeReactTsWebsiteProject 那样捆绑 cognito 身份验证,而是通过 ts#website#auth 生成器显式添加。

Terminal window
pnpm nx g @aws/nx-plugin:ts#website#auth --project=website --cognitoDomain=shopping-list --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run

这会添加 React 组件来管理适当的重定向,以确保用户使用 Cognito 托管 UI 登录。这还会在 packages/common/constructs 中添加一个 CDK 构造来部署 Cognito 资源,称为 UserIdentity

在 PDK 中,您可以将提供的 Projen 项目相互传递以触发生成集成代码。这在购物清单应用程序中用于配置网站以便能够与 API 集成。

使用 Nx Plugin for AWS,API 集成通过 connection 生成器支持。接下来,我们使用此生成器,以便我们的网站可以调用我们的 Smithy API:

Terminal window
pnpm nx g @aws/nx-plugin:connection --sourceProject=website --targetProject=api --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:connection --sourceProject=website --targetProject=api --no-interactive --dry-run

这会为您的网站生成必要的客户端提供程序和构建目标,以通过生成的 TypeScript 客户端调用您的 API。

CloudscapeReactTsWebsiteProject 自动包含对 @aws-northstar/ui 的依赖项,该依赖项在我们的购物清单应用程序中使用,因此我们将其添加到 @shopping-list/website 项目:

Terminal window
pnpm add @aws-northstar/ui --filter website

@aws-northstar/ui 捆绑了一个代码编辑器组件,该组件依赖于 ace-builds,使用 Vite 无法解析的 webpack 特定导入。由于我们的购物清单应用程序不使用此组件,我们通过将其添加到 packages/website/vite.config.mts 中现有 build 选项内的 external 配置中,将其从捆绑包中排除:

packages/website/vite.config.mts
build: {
outDir: '../../dist/packages/website/bundle',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
rollupOptions: {
external: ['ace-builds/webpack-resolver'],
},
},

购物清单应用程序有一个名为 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 是我们的主页,我们使用 TanStack router 的基于文件的路由。

  3. packages/website/src/pages/ShoppingList/index.tsx 复制到 packages/website/src/routes/$shoppingListId.tsx,因为 ShoppingList 是我们想要在 /:shoppingListId 路由上显示的页面。

请注意,您现在会在 IDE 中看到一些构建错误,我们需要进行一些更多的更改以适应新框架,如下所述。

从 React Router 迁移到 TanStack Router

Section titled “从 React Router 迁移到 TanStack Router”

由于我们使用的是基于文件的路由,我们可以使用网站本地开发服务器来管理自动生成路由配置。

让我们启动本地网站服务器:

Terminal window
pnpm nx dev website

您会看到一些错误,但本地网站服务器应该在端口 4200 上启动,以及本地 Smithy API 服务器在端口 3001 上启动。

按照以下步骤在 routes/index.tsxroutes/$shoppingListId.tsx 中迁移到 TanStack Router:

  1. 添加 createFileRoute 以注册每个路由:

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

    保存文件后,您会注意到对 createFileRoute 的调用的类型错误已消失。

  2. 替换 useNavigate 钩子。

    更新导入:

    import { useNavigate } from 'react-router-dom';
    import { useNavigate } from '@tanstack/react-router';

    更新对 navigate 方法(由 useNavigate 返回)的调用以传递类型安全的路由:

    navigate(`/${cell.shoppingListId}`);
    navigate({
    to: '/$shoppingListId',
    params: { shoppingListId: cell.shoppingListId },
    });
  3. 替换 useParams 钩子。

    删除导入:

    import { useParams } from 'react-router-dom';

    使用上面创建的 Route 提供的钩子更新对 useParams 的调用。这些现在是类型安全的!

    const { shoppingListId } = useParams();
    const { shoppingListId } = Route.useParams();

由于我们的路由文件在文件树中的嵌套深度不如 PDK 项目中的深度,我们需要在 routes/index.tsxroutes/$shoppingListId.tsx 中修复 CreateItem 的导入:

import CreateItem from "../../components/CreateItem";
import CreateItem from "../components/CreateItem";

AppLayoutContext 在我们的新项目中也在稍微不同的位置提供:

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

迁移以使用新生成的 TypeScript 客户端

Section titled “迁移以使用新生成的 TypeScript 客户端”

我们现在越来越接近了!接下来,我们需要迁移以使用 Nx Plugin for AWS 提供的 TypeScript 客户端,与 Type Safe API 相比,它有一些改进。要实现这一点,请按照以下步骤操作

  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";

    请注意,routes/$shoppingListId.tsxShoppingList 类型导入为 _ShoppingList - 在该文件中我们应该做同样的事情,但同样从 types.gen 导入。

    还要注意,我们直接从 @tanstack/react-query 导入相关的钩子,因为生成的客户端提供了为 TanStack 查询钩子生成选项的方法,而不是钩子包装器。

  2. 实例化新的 TanStack Query 钩子,例如:

    const getShoppingLists = useGetShoppingLists({ pageSize: PAGE_SIZE });
    const putShoppingList = usePutShoppingList();
    const deleteShoppingList = useDeleteShoppingList();
    const api = useMyApi();
    const getShoppingLists = useInfiniteQuery(
    api.getShoppingLists.infiniteQueryOptions(
    { pageSize: PAGE_SIZE },
    { getNextPageParam: (p) => p.nextToken },
    ),
    );
    const putShoppingList = useMutation(api.putShoppingList.mutationOptions());
    const deleteShoppingList = useMutation(
    api.deleteShoppingList.mutationOptions(),
    );
  3. 删除对在请求正文中接受参数的操作的调用的包装器 <operation>RequestContent

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

由于 TanStack Query v4(PDK 使用)和 connection 生成器添加的 v5 之间的差异,还有一些错误需要修复:

  1. 对于变更,将 isLoading 替换为 isPending,例如:

    putShoppingList.isLoading
    putShoppingList.isPending
  2. 购物清单应用程序使用了 @aws-northstar/ui 中的 InfiniteQueryTable,它期望来自 TanStack Query v4 的类型。这实际上适用于 v5 的无限查询,因此我们可以只抑制类型错误:

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

您现在可以访问 http://localhost:4200/ 上的本地网站

现在一切都已迁移,网站应该可以加载了!由于购物清单应用程序除了 API、网站和身份之外唯一依赖的基础设施是 DynamoDB 表 - 如果您在区域内有一个名为 shopping_list 的 DynamoDB 表,并且本地 AWS 凭证可以访问它,网站将完全正常运行!

如果没有,没关系,我们接下来将迁移基础设施。

点击此处查看教程中两个购物清单页面的完整前后示例

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

除了 Projen 项目之外,PDK 还提供了这些项目所依赖的 CDK 构造。我们也将把购物清单应用程序从这些 CDK 构造迁移出来,转而使用 Nx Plugin for AWS 生成的构造。

运行 ts#infra 生成器packages/infra 中设置您的基础设施项目:

Terminal window
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run

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

  • DatabaseConstruct 用于存储购物清单的 DynamoDB 表
  • UserIdentity 用于 Cognito 资源,直接从 PDK 导入
  • MyApi 用于部署 Smithy API,它使用生成的 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 中有一个构造,它实例化了 Type Safe API 从您的 Smithy 模型生成的 CDK 构造。

除了这个构造之外,由于 PDK 项目使用了 @handler 特性,还生成了 lambda 函数 CDK 构造。

与 Type Safe API 一样,Nx Plugin for AWS 基于您的 Smithy 模型为集成提供类型安全,但它以更简单、更灵活的方式实现。它不是在构建时生成整个 CDK 构造,而是只生成最少的”元数据”,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 购物清单应用程序中,DatabaseConsruct 被传递到 MyApi 中,它管理向每个生成的函数构造添加相关权限。我们将通过访问 Api 构造的类型安全 integrations 属性,直接在 application-stack.ts 文件中执行此操作:

    stacks/application-stack.ts
    // Grant our lambda functions scoped access to call 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 的 IAM 权限。我们将在 application-stack.ts 中执行等效操作:

    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 表,以及包含所有注册用户详细信息的用户池。我们的高层计划是保留这两个关键资源并将它们移动到由新堆栈管理,然后更新 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 查看我们将被提示导入哪些资源。

    New Application
    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): enter RoleName (empty to skip)
    shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/Resource (AWS::Cognito::UserPool): enter UserPoolId (empty to skip)
    shopping-list-infra-sandbox/Application/Database/ShoppingList/Resource (AWS::DynamoDB::Table): import with 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 Application
    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 construct accepts UserPool not IUserPool, but this still works!
    userPool: userPool as any,
    });
  10. 再次部署您的 PDK 项目,这意味着资源将不再由我们的 PDK 项目的 CloudFormation 堆栈管理。

    PDK Application
    cd packages/infra
    npx projen deploy
  11. 现在资源已不受管理,我们可以在新应用程序中运行 cdk import 来实际执行导入:

    New Application
    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 并使用了网站和 API 的自动生成域,您始终可以考虑代理请求(例如通过 CloudFront HTTP 源API Gateway HTTP 集成)。

  15. 监控 PDK 应用程序指标以确保没有流量,最后销毁旧的 CloudFormation 堆栈:

    Terminal window
    cd packages/infra
    npx projen destroy

这涉及的内容相当多,但我们成功地将用户无缝迁移到了新应用程序!🎉🎉🎉

我们现在拥有了 Nx Plugin for AWS 相对于 PDK 的新优势:

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

本节为上述示例迁移未涵盖的 PDK 功能提供指导。

作为从 PDK 迁移的一般规则,我们建议使用 Nx Workspace 启动任何项目,因为它与 PDK Monorepo 相似。我们还建议使用我们的生成器作为构建任何新类型的基础。

Terminal window
pnpm create @aws/nx-workspace my-project

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 构造来封装一些 CDK Pipelines 资源。

要从中迁移,您可以直接使用 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 Server Generators 之一。这些不会直接为 AWS Lambda 生成代码,但您可以使用 AWS Lambda Web Adapter 来弥补其中许多工具的差距。

对于 TypeScript 客户端,您可以使用 ts#website 生成器connection 生成器,并结合一个示例 ts#api(将 framework 设置为 smithy)来查看客户端是如何生成并与网站集成的。这会配置构建目标,通过调用我们的 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 支持的所有三种语言提供了自己的客户端代码生成器:

上述 OpenAPI 部分也适用,因为 TypeSpec 可以编译为 OpenAPI。

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

Smithy code generator for Java。它有一个 Java 服务器生成器以及一个适配器用于在 AWS Lambda 上运行生成的 Java 服务器。

Smithy 没有 Python 的服务器生成器,因此您需要通过 OpenAPI。请参阅上述关于使用 OpenAPI 建模的 API 的部分以了解可能的选项。

Smithy code generator for 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 模型的项目,这些模型可以被多个基于 Smithy 的 API 重用。

等效的是将 type 设置为 shapessmithy#project 生成器

Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes --dry-run

将您的 SmithyShapeLibraryProject 中的形状移动到生成项目的 src 文件夹中,然后参考 Smithy 项目指南了解如何将库连接为 API 模型的依赖项。

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-docs 向您的 model 项目添加文档生成目标,例如:

    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 documentation generators

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

您可以迁移到 JSON Schema Faker,它可以基于 JSON Schema 创建模拟数据。这可以直接作用于 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 Server SDK,您需要为您的服务/路由器创建”存根”以便您的服务能够编译,例如:

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

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

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

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

不幸的是,对于 Type Safe API 的 websocket API,使用 API Gateway 和 Lambda 进行模型驱动的 API 开发没有直接的迁移路径。但是,本指南的这一部分至少旨在提供一些想法。

考虑使用 AsyncAPI 来建模您的 API,而不是 OpenAPI 或 TypeSpec,因为它专为处理异步 API 而设计。AsyncAPI NodeJS Template 可以生成一个 Node websocket 后端,您可以将其托管在 ECS 上,例如。

您还可以考虑使用 AppSync Events 作为基础设施,并使用 Powertools这篇博客文章值得一读!

另一个选项是在 AppSync 上使用带有 websocket 的 GraphQL API,我们有一个 GitHub issue 您可以点赞!有关详细信息和示例项目的链接,请参阅 AppSync 开发者指南

您还可以考虑构建自己的代码生成器,解释与 Type Safe API 相同的供应商扩展。有关构建基于 OpenAPI 的自定义代码生成器的详细信息,请参阅使用 OpenAPI 建模的 API 部分。您可以在这里找到 Type Safe API 用于 API Gateway Websocket API Lambda 处理程序的模板,以及在这里找到客户端模板。

您还可以考虑迁移以使用 ts#trpc-api 生成器来使用 tRPC。在撰写本文时,我们还没有对订阅/流的支持,但如果这是您需要的功能,请为我们的 GitHub issue 点赞。

Smithy 是协议无关的,但尚不支持 Websocket 协议,请参阅此 GitHub issue 跟踪支持

PDK 支持用 Python 和 Java 编写的 CDK 基础设施。在撰写本文时,我们在 Nx Plugin for AWS 中不支持这一点。

建议的前进路径是将您的 CDK 基础设施迁移到 TypeScript,或者使用我们的生成器并将通用构造包迁移到您所需的语言。您可以使用生成式 AI 来加速这类迁移,例如 Kiro CLI。您可以让 AI 代理迭代迁移,直到合成的 CloudFormation 模板完全相同。

这同样适用于 Type Safe API 在 Python 或 Java 中生成的基础设施 - 您可以从通用构造包中转换通用的 rest-api.ts 构造,并为您的目标语言实现您自己的简单元数据生成器(请参阅 使用 OpenAPI 建模的 API 部分)。

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

PDK 是基于 Projen 构建的。Projen 和 Nx Generators 有着相当根本的差异,这意味着虽然技术上可以将它们结合起来,但这很可能是一种反模式。Projen 将项目文件作为代码进行管理,使得它们不能被直接修改,而 Nx generators 只生成一次项目文件,然后代码可以自由修改。

如果您想继续使用 Projen,您可以自己实现所需的 Projen 项目类型。要遵循 Nx Plugin for AWS 的模式,您可以运行我们的 generators 或在 GitHub 上查看它们的源代码,以了解您所需的项目类型是如何构建的,然后使用 Projen 的原语实现相关部分。

介绍 Nx Plugin for AWS MCP Server

在快速发展的软件开发领域中,AI 助手已成为我们编码旅程中的宝贵合作伙伴。许多开发者已经接受了我们亲切地称之为”氛围编码”的方式——人类创造力与 AI 辅助之间的协作舞蹈。像任何新兴实践一样,它既带来了令人兴奋的好处,也带来了显著的挑战。本文介绍了 Nx Plugin for AWS MCP Server,它增强了在使用 AWS 产品和服务时的 AI 辅助开发体验。

氛围编码,即与 AI 助手协作构建软件的实践,已经改变了许多组织处理软件开发的方式。你描述想要构建的内容,你的 AI 助手通过编写代码和测试、运行构建命令以及协作迭代来帮助实现你的愿景,从而完成大大小小的任务。

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

尽管有其好处,氛围编码也存在可能打断你的工作流程并导致挫败感的陷阱。AI 工具可能在项目中产生不一致的模式,这可能会在后续造成维护难题。如果没有具体的指导,AI 可能会错过经验丰富的开发者自然会纳入的重要 AWS 特定最佳实践或安全考虑。

如果没有清晰的项目结构,AI 辅助的代码可能会变得杂乱无章且难以维护。AI 可能会为已经有既定解决方案的问题创建自定义实现,不必要地重新发明轮子。

这些挑战可能导致技术债务、安全漏洞和挫败感,特别是在使用各种相互连接的 AWS 服务时,而不仅仅是在单个框架的范围内。

Nx Plugin for AWS 为使用 Nx monorepo 工具构建 AWS 应用程序提供了结构化的基础。该插件不是从空白画布开始,而是为项目组织提供了一致的框架。

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

通过提供这种结构,Nx Plugin for AWS 为 AI 助手提供了一个清晰的工作结构。AI 助手可以遵循既定的约定,而不是从头开始发明模式,从而产生更一致和可维护的代码库。

Model Context Protocol (MCP) 是一个开放标准,允许 AI 助手与外部工具和资源交互。Nx Plugin for AWS MCP server 通过关于 Nx Plugin for AWS 的专业知识扩展了你的 AI 助手的能力。

MCP server 提供关于最佳实践、可用项目结构以及特定于 AWS 开发的实现模式的上下文信息。它使你的 AI 工具能够创建工作空间并运行生成器来搭建常见项目类型的脚手架。这种上下文感知帮助 AI 做出更明智的建议,这些建议与既定模式保持一致并避免常见陷阱。

你的 AI 助手可以利用 MCP server 为你的项目奠定基础,而不是生成可能不符合最佳实践或可能引用不存在功能的代码。结果是更具确定性和可靠性的开发体验,你可以从项目核心组件的坚实基础开始,并使用 AI 来填充业务逻辑。

如果你有兴趣探索具有更多结构和可靠性的 AI 辅助 AWS 开发,请尝试 Nx Plugin for AWS MCP Server。你可以使用以下 MCP Server 配置在你喜欢的 AI 助手(Kiro、Kiro CLI、Cline、Claude Code 等)中设置它:

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

有关详细说明,请参阅我们的使用 AI 构建指南

欢迎使用 @aws/nx-plugin

我们正式上线了!🚀

Nx Plugin for AWS 是一个 Nx 插件,提供了一套工具包,用于简化在 AWS 上构建和部署全栈应用程序的过程。它为开发人员提供了预配置的应用程序和 IaC 代码模板,显著减少了设置和配置所需的时间。该插件处理了 AWS 服务集成的复杂性,同时保持了自定义的灵活性。

用户只需从可用生成器列表中选择他们想要的组件,提供任何配置选项,然后让 @aws/nx-plugin 生成所需的启动代码。此工具包中存在多个生成器,可以创建 API、网站、基础设施,甚至可以做更复杂的事情,比如将前端集成到后端(包括通过 AST 转换更新现有文件!)并使用类型安全的客户端。

generator

要了解更多信息,请从我们的地下城冒险教程开始,该教程涵盖了插件的所有主要组件,应该能让您很好地了解如何使用它。

我们非常希望听到您的反馈,请随时发布讨论提出问题,让我们知道您的想法以及您希望接下来看到什么!

立即试用!