Bỏ qua để đến nội dung

Di chuyển từ AWS PDK

Hướng dẫn này sẽ đưa bạn qua một ví dụ về việc di chuyển dự án AWS PDK sang Nx Plugin for AWS, đồng thời cung cấp hướng dẫn chung về chủ đề này.

Di chuyển sang Nx Plugin for AWS mang lại các lợi ích sau so với PDK:

  • Xây dựng nhanh hơn
  • Dễ sử dụng hơn (UI và CLI)
  • Thân thiện với vibe-coding (thử MCP server của chúng tôi!)
  • Công nghệ hiện đại hơn
  • Phát triển API và website cục bộ
  • Kiểm soát tốt hơn (sửa đổi các file được cung cấp để phù hợp với trường hợp sử dụng của bạn)
  • Và nhiều hơn nữa!

Ví dụ Di chuyển: Ứng dụng Danh sách Mua sắm

Phần tiêu đề “Ví dụ Di chuyển: Ứng dụng Danh sách Mua sắm”

Trong hướng dẫn này, chúng ta sẽ sử dụng Ứng dụng Danh sách Mua sắm từ Hướng dẫn PDK làm dự án mục tiêu để di chuyển. Làm theo các bước trong hướng dẫn đó để tạo dự án mục tiêu nếu bạn muốn tự mình làm theo.

Ứng dụng danh sách mua sắm bao gồm các loại dự án PDK sau:

  • MonorepoTsProject
  • TypeSafeApiProject
  • CloudscapeReactTsWebsiteProject
  • InfrastructureTsProject

Để bắt đầu, chúng ta sẽ tạo một workspace mới cho dự án mới của mình. Mặc dù cực đoan hơn so với di chuyển tại chỗ, cách tiếp cận này mang lại cho chúng ta kết quả cuối cùng sạch sẽ nhất. Tạo một Nx workspace tương đương với việc sử dụng MonorepoTsProject của PDK:

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

Mở thư mục shopping-list mà lệnh này tạo ra trong IDE yêu thích của bạn.

TypeSafeApiProject được sử dụng trong ứng dụng danh sách mua sắm đã tận dụng:

  • Smithy làm ngôn ngữ mô hình hóa
  • TypeScript để triển khai các operation
  • Tạo hook TypeScript để tích hợp với website react

Do đó, chúng ta có thể sử dụng generator ts#smithy-api để cung cấp chức năng tương đương.

Chạy generator ts#smithy-api để thiết lập dự án api của bạn trong packages/api:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#smithy-api
  5. Fill in the required parameters
    • name: api
    • namespace: com.aws
    • auth: IAM
  6. Click Generate

Bạn sẽ nhận thấy điều này tạo ra một dự án model, cũng như một dự án backend. Dự án model chứa mô hình Smithy của bạn, và backend chứa triển khai máy chủ của bạn.

Backend sử dụng Smithy Server Generator for TypeScript. Chúng ta sẽ khám phá điều này chi tiết hơn bên dưới.

Bây giờ chúng ta đã có cấu trúc cơ bản cho dự án Smithy API, chúng ta có thể di chuyển mô hình:

  1. Xóa các tệp Smithy ví dụ đã được tạo trong packages/api/model/src

  2. Sao chép mô hình của bạn từ thư mục packages/api/model/src/main/smithy của dự án PDK vào thư mục packages/api/model/src của dự án mới.

  3. Cập nhật tên dịch vụ và namespace trong smithy-build.json để khớp với ứng dụng PDK:

    smithy-build.json
    "plugins": {
    "openapi": {
    "service": "com.aws#MyApi",
    ...
  4. Cập nhật dịch vụ trong main.smithy để thêm lỗi ValidationException, điều này là bắt buộc khi sử dụng 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. Thêm tệp extensions.smithy vào packages/api/model/src nơi chúng ta sẽ định nghĩa một trait cung cấp thông tin phân trang cho client được tạo:

    extensions.smithy
    $version: "2"
    namespace com.aws
    use smithy.openapi#specificationExtension
    @trait
    @specificationExtension(as: "x-cursor")
    structure cursor {
    inputToken: String
    enabled: Boolean
    }
  6. Thêm trait @cursor mới vào operation GetShoppingLists trong get-shopping-lists.smithy:

    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
    }

    Bất kỳ operation @paginated nào cũng nên sử dụng @cursor nếu bạn đang sử dụng client generator được cung cấp bởi Nx Plugin for AWS (thông qua generator api-connection).

  7. Cuối cùng, xóa trait @handler khỏi tất cả các operation vì điều này không được hỗ trợ bởi Nx Plugin for AWS. Khi sử dụng ts#smithy-api, chúng ta không cần các construct CDK hàm lambda được tự động tạo và các target đóng gói được tạo bởi trait này, vì chúng ta sử dụng một bundle duy nhất cho tất cả các hàm lambda.

Tại thời điểm này, hãy chạy một bản build để kiểm tra các thay đổi mô hình của chúng ta và đảm bảo rằng chúng ta có một số mã máy chủ được tạo để làm việc. Sẽ có một số lỗi trong dự án backend (@shopping-list/api) nhưng chúng ta sẽ giải quyết chúng tiếp theo.

Terminal window
pnpm nx run-many --target build

Bạn có thể coi dự án api/backend phần nào tương đương với dự án api/handlers/typescript của Type Safe API.

Một trong những khác biệt chính giữa Type Safe API và generator ts#smithy-api là các handler được triển khai bằng cách sử dụng Smithy Server Generator for TypeScript, thay vì các wrapper handler được tạo riêng của Type Safe API (được tìm thấy trong dự án api/generated/typescript/runtime).

Các lambda handler của ứng dụng danh sách mua sắm phụ thuộc vào package @aws-sdk/client-dynamodb, vì vậy hãy cài đặt nó trước:

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

Sau đó, hãy sao chép tệp handlers/src/dynamo-client.ts từ dự án PDK vào backend/src/operations để nó có sẵn cho các handler của chúng ta.

Để di chuyển các handler, bạn có thể làm theo các bước chung sau:

  1. Sao chép handler từ thư mục packages/api/handlers/typescript/src của dự án PDK sang thư mục packages/api/backend/src/operations của dự án mới.

  2. Xóa các import my-api-typescript-runtime và thay vào đó import kiểu operation từ TypeScript Server SDK được tạo, cũng như ServiceContext ví dụ:

    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. Xóa export wrapper handler

    export const handler = deleteShoppingListHandler(
    ...INTERCEPTORS,
    deleteShoppingList,
    );
  4. Cập nhật chữ ký cho operation handler của bạn để sử dụng SSDK:

    export const deleteShoppingList: DeleteShoppingListChainedHandlerFunction = async (request) => {
    export const DeleteShoppingList: DeleteShoppingListOperation<ServiceContext> = async (input, ctx) => {
  5. Thay thế việc sử dụng LoggingInterceptor bằng ctx.logger. (Cũng áp dụng cho các interceptor metrics và tracing):

    LoggingInterceptor.getLogger(request).info('...');
    ctx.logger.info('...');
  6. Cập nhật các tham chiếu đến tham số đầu vào. Vì SSDK cung cấp các kiểu khớp chính xác với mô hình Smithy của bạn (thay vì nhóm các tham số path/query/header riêng biệt với tham số body), hãy cập nhật mọi tham chiếu đầu vào tương ứng:

    const shoppingListId = request.input.requestParameters.shoppingListId;
    const shoppingListId = input.shoppingListId;
  7. Xóa việc sử dụng Response. Thay vào đó, chúng ta chỉ trả về các đối tượng thuần túy trong SSDK.

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

    Chúng ta cũng không còn throw hoặc return Response, thay vào đó chúng ta throw các lỗi được tạo của 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. Cập nhật mọi import để sử dụng cú pháp ESM, cụ thể là thêm phần mở rộng .js vào các import tương đối.

  9. Thêm operation vào 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,
    };
Nhấp vào đây để xem ví dụ trước/sau đầy đủ cho ba operation danh sách mua sắm từ hướng dẫn

Chúng ta đã tạo dự án Smithy API với tên api ban đầu vì chúng ta muốn nó được thêm vào packages/api để đảm bảo tính nhất quán với dự án PDK. Vì Smithy API của chúng ta bây giờ định nghĩa service MyApi thay vì service Api, chúng ta cần cập nhật bất kỳ instance nào của getApiServiceHandler thành getMyApiServiceHandler.

Thực hiện thay đổi này trong 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);

Và trong 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);

Ngoài ra, cập nhật packages/api/backend/project.json và cập nhật metadata.apiName thành 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]
},

Bây giờ chúng ta có thể build dự án để kiểm tra xem việc di chuyển đã hoạt động đến đâu:

Terminal window
pnpm nx run-many --target build

CloudscapeReactTsWebsiteProject được sử dụng trong ứng dụng danh sách mua sắm đã cấu hình một trang web React với CloudScape và xác thực Cognito được tích hợp sẵn.

Loại dự án này tận dụng create-react-app, hiện đã không còn được hỗ trợ. Để di chuyển trang web trong hướng dẫn này, chúng ta sẽ sử dụng trình tạo ts#react-website, sử dụng các công nghệ hiện đại và được hỗ trợ hơn, cụ thể là Vite.

Trong quá trình di chuyển, chúng ta cũng sẽ chuyển từ React Router được cấu hình sẵn của PDK sang TanStack Router, điều này bổ sung thêm tính an toàn về kiểu cho việc định tuyến trang web.

Chạy trình tạo ts#react-website để thiết lập dự án trang web của bạn trong packages/website:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#react-website
  5. Fill in the required parameters
    • name: website
  6. Click Generate

Trình tạo trang web React ở trên không đi kèm xác thực cognito theo mặc định như CloudscapeReactTsWebsiteProject, thay vào đó nó được thêm vào một cách rõ ràng thông qua trình tạo ts#react-website#auth.

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#react-website#auth
  5. Fill in the required parameters
    • project: website
    • cognitoDomain: shopping-list
  6. Click Generate

Điều này thêm các component React quản lý các chuyển hướng phù hợp để đảm bảo người dùng đăng nhập bằng giao diện người dùng được lưu trữ của Cognito. Điều này cũng thêm một construct CDK để triển khai các tài nguyên Cognito trong packages/common/constructs, được gọi là UserIdentity.

Trong PDK, bạn có thể truyền các dự án Projen được cung cấp cho nhau để kích hoạt mã tích hợp được tạo ra. Điều này đã được sử dụng trong ứng dụng danh sách mua sắm để cấu hình trang web có thể tích hợp với API.

Với Nx Plugin for AWS, tích hợp API được hỗ trợ thông qua trình tạo api-connection. Tiếp theo, chúng ta sử dụng trình tạo này để trang web của chúng ta có thể gọi Smithy API:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - api-connection
  5. Fill in the required parameters
    • sourceProject: website
    • targetProject: api
  6. Click Generate

Điều này tạo ra các provider client cần thiết và các target build để trang web của bạn có thể gọi API của bạn thông qua một client TypeScript được tạo.

CloudscapeReactTsWebsiteProject tự động bao gồm phụ thuộc vào @aws-northstar/ui được sử dụng trong ứng dụng danh sách mua sắm của chúng ta, vì vậy chúng ta thêm nó ở đây:

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

Ứng dụng danh sách mua sắm có một component có tên CreateItem, và hai page, ShoppingListShoppingLists. Chúng ta sẽ di chuyển chúng sang trang web mới, thực hiện một số điều chỉnh vì chúng ta đang sử dụng TanStack Router và trình tạo mã client TypeScript của Nx Plugin for AWS.

  1. Sao chép packages/website/src/components/CreateItem/index.tsx từ dự án PDK vào chính xác cùng vị trí trong dự án mới.

  2. Sao chép packages/website/src/pages/ShoppingLists/index.tsx sang packages/website/src/routes/index.tsx, vì ShoppingLists là trang chủ của chúng ta và chúng ta sử dụng định tuyến dựa trên file với TanStack router.

  3. Sao chép packages/website/src/pages/ShoppingList/index.tsx sang packages/website/src/routes/$shoppingListId.tsx, vì ShoppingList là trang chúng ta muốn hiển thị trên route /:shoppingListId.

Lưu ý rằng bây giờ bạn sẽ có một số lỗi build hiển thị trong IDE của mình, chúng ta sẽ cần thực hiện thêm một vài thay đổi để phù hợp với framework mới, được nêu dưới đây.

Di Chuyển từ React Router sang TanStack Router

Phần tiêu đề “Di Chuyển từ React Router sang TanStack Router”

Vì chúng ta đang sử dụng định tuyến dựa trên file, chúng ta có thể sử dụng máy chủ phát triển cục bộ của trang web để quản lý việc tự động tạo cấu hình route. Hãy khởi động máy chủ trang web cục bộ:

Terminal window
pnpm nx serve-local website

Bạn sẽ thấy một số lỗi, nhưng máy chủ trang web cục bộ sẽ khởi động trên cổng 4200, cũng như máy chủ Smithy API cục bộ trên cổng 3001.

Làm theo các bước dưới đây trong cả routes/index.tsxroutes/$shoppingListId.tsx để di chuyển sang TanStack Router:

  1. Thêm createFileRoute để đăng ký mỗi route:

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

    Sau khi bạn lưu file, bạn sẽ nhận thấy rằng các lỗi kiểu với lời gọi createFileRoute đã biến mất.

  2. Thay thế hook useNavigate.

    Cập nhật import:

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

    Cập nhật các lời gọi đến phương thức navigate (được trả về bởi useNavigate) để truyền các route an toàn về kiểu:

    navigate(`/${cell.shoppingListId}`);
    navigate({
    to: '/$shoppingListId',
    params: { shoppingListId: cell.shoppingListId },
    });
  3. Thay thế hook useParams.

    Xóa import:

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

    Cập nhật các lời gọi đến useParams với hook được cung cấp bởi Route đã tạo ở trên. Bây giờ chúng an toàn về kiểu!

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

Vì các file route của chúng ta không còn lồng sâu trong cây thư mục như trong dự án PDK, chúng ta cần sửa import cho CreateItem trong cả routes/index.tsxroutes/$shoppingListId.tsx:

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

AppLayoutContext cũng được cung cấp ở một vị trí hơi khác trong dự án mới của chúng ta:

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

Di Chuyển để Sử Dụng Client TypeScript Được Tạo Mới

Phần tiêu đề “Di Chuyển để Sử Dụng Client TypeScript Được Tạo Mới”

Chúng ta đang đến gần rồi! Tiếp theo, chúng ta cần di chuyển để sử dụng client TypeScript được cung cấp bởi Nx Plugin for AWS, có một số cải tiến so với Type Safe API. Để đạt được điều này, hãy làm theo các bước dưới đây

  1. Import client và kiểu mới được tạo thay vì cái cũ, ví dụ:

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

    Lưu ý rằng routes/$shoppingListId.tsx import kiểu ShoppingList dưới dạng _ShoppingList - trong file đó chúng ta nên làm tương tự, nhưng lại import từ types.gen.

    Cũng lưu ý rằng chúng ta import các hook liên quan trực tiếp từ @tanstack/react-query, vì client được tạo cung cấp các phương thức để tạo tùy chọn cho các hook TanStack query, thay vì các wrapper hook.

  2. Khởi tạo các hook TanStack Query mới, ví dụ:

    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. Xóa wrapper <operation>RequestContent cho các lời gọi đến các operation chấp nhận tham số trong request body:

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

Còn một vài lỗi cần sửa do sự khác biệt giữa TanStack Query v4 (được sử dụng bởi PDK) và v5 mà trình tạo api-connection đã thêm:

  1. Thay thế isLoading bằng isPending cho các mutation, ví dụ:

    putShoppingList.isLoading
    putShoppingList.isPending
  2. Ứng dụng danh sách mua sắm sử dụng InfiniteQueryTable từ @aws-northstar/ui mong đợi một kiểu từ TanStack Query v4. Điều này thực sự hoạt động với các infinite query từ v5, vì vậy chúng ta chỉ cần loại bỏ lỗi kiểu:

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

Bây giờ bạn có thể truy cập trang web cục bộ tại http://localhost:4200/

Trang web sẽ tải lên bây giờ sau khi mọi thứ đã được di chuyển! Vì cơ sở hạ tầng duy nhất mà ứng dụng danh sách mua sắm dựa vào ngoài API, Website và Identity là bảng DynamoDB - nếu bạn có bảng DynamoDB có tên shopping_list trong region, và thông tin xác thực AWS cục bộ có thể truy cập nó, trang web sẽ hoạt động đầy đủ!

Nếu không, không sao, chúng ta sẽ di chuyển cơ sở hạ tầng tiếp theo.

Nhấp vào đây để xem ví dụ đầy đủ trước/sau cho hai trang danh sách mua sắm từ hướng dẫn

Dự án cuối cùng chúng ta cần di chuyển cho ứng dụng danh sách mua sắm của mình là InfrastructureTsProject. Đây là một dự án TypeScript CDK, mà tương đương với Nx Plugin for AWS là trình tạo ts#infra.

Cũng như các dự án Projen, PDK cũng cung cấp các CDK construct mà các dự án này phụ thuộc vào. Chúng ta sẽ di chuyển ứng dụng danh sách mua sắm khỏi các CDK construct này, thay vào đó sử dụng những construct được tạo bởi Nx Plugin for AWS.

Chạy trình tạo ts#infra để thiết lập dự án hạ tầng của bạn trong packages/infra:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#infra
  5. Fill in the required parameters
    • name: infra
  6. Click Generate

Ứng dụng danh sách mua sắm PDK đã khởi tạo các construct sau trong CDK application stack:

  • DatabaseConstruct cho bảng DynamoDB lưu trữ danh sách mua sắm
  • UserIdentity cho các tài nguyên Cognito, được import trực tiếp từ PDK
  • MyApi để triển khai Smithy API, sử dụng TypeScript CDK construct được tạo ra với các tích hợp type-safe, phụ thuộc vào CDK construct TypeSafeRestApi của PDK bên dưới.
  • Website để triển khai Website, bao bọc CDK construct StaticWebsite của PDK.

Tiếp theo, chúng ta sẽ di chuyển từng phần này sang dự án mới.

Sao chép packages/infra/src/stacks/application-stack.ts từ ứng dụng danh sách mua sắm PDK đến vị trí chính xác tương tự trong dự án mới của bạn. Bạn sẽ thấy một số lỗi TypeScript mà chúng ta sẽ xử lý bên dưới.

Ứng dụng danh sách mua sắm PDK có một construct Database trong packages/src/constructs/database.ts. Sao chép nó đến vị trí chính xác tương tự trong dự án mới của bạn.

Vì Nx Plugin for AWS sử dụng Checkov cho các bài kiểm tra bảo mật nghiêm ngặt hơn một chút so với PDK Nag, chúng ta cũng cần thêm một số suppression:

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',
);

Trong application-stack.ts, cập nhật import cho DatabaseConstruct để sử dụng cú pháp ESM:

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

Construct UserIdentity thường có thể được thay thế mà không cần thay đổi bằng cách điều chỉnh các import.

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

Lưu ý rằng các construct cơ bản được sử dụng bởi construct UserIdentity mới được cung cấp trực tiếp từ aws-cdk-lib, trong khi PDK sử dụng @aws-cdk/aws-cognito-identitypool-alpha.

Ứng dụng danh sách mua sắm PDK có một construct trong constructs/apis/myapi.ts khởi tạo một CDK construct mà Type Safe API đã tạo ra từ mô hình Smithy của bạn.

Cũng như construct này, vì dự án PDK sử dụng trait @handler, các CDK construct lambda function được tạo ra cũng được sinh ra.

Giống như Type Safe API, Nx Plugin for AWS cung cấp type-safety cho các tích hợp dựa trên mô hình Smithy của bạn, tuy nhiên nó được thực hiện theo cách đơn giản và linh hoạt hơn nhiều. Thay vì tạo ra toàn bộ một CDK construct tại thời điểm build, chỉ có “metadata” tối thiểu được tạo ra, mà packages/common/constructs/src/app/apis/api.ts sử dụng theo cách generic. Bạn có thể tìm hiểu thêm về cách sử dụng construct trong hướng dẫn trình tạo ts#smithy-api.

Làm theo các bước dưới đây:

  1. Khởi tạo construct Api trong application-stack.ts

    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(),
    });

    Lưu ý ở đây chúng ta sử dụng Api.defaultIntegrations(this).build() - hành vi mặc định là tạo một lambda function cho mỗi operation trong API của chúng ta, đây là hành vi tương tự mà chúng ta đã có trong myapi.ts.

  2. Cấp quyền cho các lambda function để truy cập bảng DynamoDB.

    Trong ứng dụng danh sách mua sắm PDK, DatabaseConsruct được truyền vào MyApi, và nó quản lý việc thêm các quyền liên quan vào mỗi function construct được tạo ra. Chúng ta sẽ làm điều này trực tiếp trong file application-stack.ts bằng cách truy cập thuộc tính integrations type-safe của construct Api:

    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. Cấp quyền cho người dùng đã xác thực để gọi API.

    Trong myapi.ts của ứng dụng PDK, người dùng đã xác thực cũng được cấp quyền IAM để gọi API. Chúng ta sẽ làm tương đương trong application-stack.ts:

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

Cuối cùng, chúng ta thêm construct Website từ packages/common/constructs/src/app/static-websites/website.ts vào application-stack.ts, vì đây là tương đương với packages/infra/src/constructs/websites/website.ts của ứng dụng danh sách mua sắm PDK.

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

Lưu ý rằng chúng ta không truyền identity hoặc API vào website - cấu hình runtime được quản lý trong mỗi construct được cung cấp bởi Nx Plugin for AWS, trong đó UserIdentityApi đăng ký các giá trị cần thiết, và Website quản lý việc triển khai nó đến /runtime-config.json trên trang web tĩnh của bạn.

Bây giờ hãy build dự án sau khi chúng ta đã di chuyển tất cả các phần liên quan của codebase sang dự án mới.

Terminal window
pnpm nx run-many --target build

Bây giờ chúng ta đã có codebase được di chuyển hoàn toàn, chúng ta có thể xem xét việc triển khai nó. Có hai hướng đi mà chúng ta có thể thực hiện tại thời điểm này.

Cách tiếp cận đơn giản nhất là coi đây là một ứng dụng hoàn toàn mới, có nghĩa là chúng ta sẽ “bắt đầu lại” với một bảng DynamoDB mới và Cognito User Pool mới - mất tất cả người dùng và danh sách mua sắm của họ. Với cách tiếp cận này, chỉ cần:

  1. Xóa bảng DynamoDB có tên shopping_list

  2. Triển khai ứng dụng mới:

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

🎉 Và chúng ta đã hoàn thành! 🎉

Di Chuyển Các Tài Nguyên Stateful Hiện Có mà Không Có Thời Gian Ngừng Hoạt Động (Phức Tạp Hơn)

Phần tiêu đề “Di Chuyển Các Tài Nguyên Stateful Hiện Có mà Không Có Thời Gian Ngừng Hoạt Động (Phức Tạp Hơn)”

Trên thực tế, nhiều khả năng bạn sẽ muốn di chuyển các tài nguyên AWS hiện có để chúng được quản lý bởi codebase mới, đồng thời tránh bất kỳ thời gian ngừng hoạt động nào cho khách hàng của bạn.

Đối với ứng dụng shopping list của chúng ta, các tài nguyên stateful mà chúng ta quan tâm là bảng DynamoDB chứa danh sách mua sắm của người dùng và User Pool chứa thông tin chi tiết của tất cả người dùng đã đăng ký. Kế hoạch tổng thể của chúng ta sẽ là giữ lại hai tài nguyên chính này và di chuyển chúng sao cho chúng được quản lý bởi stack mới của chúng ta, sau đó cập nhật DNS để trỏ đến website mới (và API nếu được expose cho khách hàng).

  1. Cập nhật ứng dụng mới của bạn để tham chiếu các tài nguyên hiện có mà bạn muốn giữ lại.

    Đối với ứng dụng shopping list, chúng ta thực hiện điều này cho bảng DynamoDB

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

    Và cho Cognito User Pool

    packages/common/constructs/src/core/user-identity.ts
    this.userPool = this.createUserPool();
    this.userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );
  2. Build và triển khai ứng dụng mới:

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

    Bây giờ chúng ta có ứng dụng mới đã được thiết lập tham chiếu các tài nguyên hiện có, chưa nhận bất kỳ traffic nào.

  3. Thực hiện kiểm tra tích hợp đầy đủ để đảm bảo ứng dụng mới hoạt động như mong đợi. Đối với ứng dụng shopping list, tải website và kiểm tra xem bạn có thể đăng nhập và tạo, xem, chỉnh sửa và xóa danh sách mua sắm không.

  4. Hoàn nguyên các thay đổi tham chiếu các tài nguyên hiện có trong ứng dụng mới của bạn, nhưng chưa triển khai chúng.

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

    Và cho Cognito User Pool

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

    Và sau đó chạy build

    Terminal window
    pnpm nx run-many --target build
  5. Sử dụng cdk import trong thư mục packages/infra của ứng dụng mới để xem các tài nguyên nào chúng ta sẽ được nhắc import.

    New Application
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    Bước qua các lời nhắc bằng cách nhấn enter. Import sẽ thất bại vì các tài nguyên được quản lý bởi một stack khác - điều này là mong đợi, chúng ta chỉ thực hiện bước này để xác nhận các tài nguyên nào chúng ta sẽ cần giữ lại. Bạn sẽ thấy output như thế này:

    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

    Điều này cho chúng ta biết rằng thực sự có 3 tài nguyên mà chúng ta sẽ cần import vào stack mới của mình.

  6. Cập nhật dự án PDK cũ của bạn để đặt RemovalPolicy thành RETAIN cho các tài nguyên được phát hiện từ bước trước. Tại thời điểm viết bài này, đây là mặc định cho cả User Pool và bảng DynamoDB, nhưng chúng ta cần cập nhật nó cho SMS Role mà chúng ta đã phát hiện ở trên:

    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. Triển khai dự án PDK của bạn để các removal policy được áp dụng

    PDK Application
    cd packages/infra
    npx projen deploy
  8. Xem console CloudFormation và ghi lại các giá trị mà bạn được nhắc trong bước cdk import ở trên

    1. User Pool ID, ví dụ us-west-2_XXXXX
    2. SMS Role Name, ví dụ infra-sandbox-UserIdentityUserPoolsmsRoleXXXXXX
  9. Cập nhật dự án PDK của bạn để tham chiếu các tài nguyên hiện có thay vì tạo chúng

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

    Và cho Cognito User Pool

    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. Triển khai lại dự án PDK của bạn, điều này có nghĩa là các tài nguyên không còn được quản lý bởi CloudFormation stack của dự án PDK của chúng ta.

    PDK Application
    cd packages/infra
    npx projen deploy
  11. Bây giờ các tài nguyên không được quản lý, chúng ta có thể chạy cdk import trong ứng dụng mới của mình để thực sự thực hiện import:

    New Application
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    Nhập các giá trị khi được nhắc, import sẽ hoàn thành thành công.

  12. Triển khai lại ứng dụng mới để đảm bảo rằng mọi thay đổi đối với các tài nguyên hiện có này (hiện được quản lý bởi stack mới của bạn) được thực hiện:

    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*
  13. Thực hiện kiểm tra đầy đủ ứng dụng mới của bạn một lần nữa

  14. Cập nhật các bản ghi DNS để trỏ đến Website mới của bạn (và API nếu cần).

    Chúng tôi khuyến nghị một cách tiếp cận từng bước bằng cách sử dụng Route53 Weighted Routing, theo đó một phần nhỏ các request được chuyển đến ứng dụng mới để bắt đầu. Khi bạn theo dõi các metrics của mình, bạn có thể tăng trọng số cho ứng dụng mới cho đến khi không có traffic nào được gửi đến ứng dụng PDK cũ của bạn.

    Nếu bạn không có DNS nào và đã sử dụng các domain được tạo tự động cho website và API, bạn luôn có thể xem xét việc proxy các request (ví dụ: thông qua CloudFront HTTP origin hoặc API Gateway HTTP integration(s)).

  15. Theo dõi các metrics của ứng dụng PDK để đảm bảo không có traffic, và cuối cùng hủy CloudFormation stack cũ:

    Terminal window
    cd packages/infra
    npx projen destroy

Điều đó phức tạp hơn khá nhiều, nhưng chúng ta đã di chuyển thành công người dùng của mình một cách liền mạch sang ứng dụng mới! 🎉🎉🎉

Bây giờ chúng ta có những lợi ích mới của Nx Plugin for AWS so với PDK:

  • Build nhanh hơn
  • Hỗ trợ phát triển API cục bộ
  • Codebase thân thiện với vibe-coding (thử MCP server của chúng tôi!)
  • Code client/server type-safe trực quan hơn
  • Và nhiều hơn nữa!

Phần này cung cấp hướng dẫn cho các tính năng của PDK không được đề cập trong ví dụ di chuyển ở trên.

Theo quy tắc chung khi chuyển từ PDK, chúng tôi khuyên bạn nên bắt đầu bất kỳ dự án nào với một Nx Workspace, do sự tương đồng của nó với PDK Monorepo. Chúng tôi cũng khuyên bạn nên sử dụng các generator của chúng tôi làm các thành phần cơ bản để xây dựng bất kỳ loại mới nào.

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

CDK Graph xây dựng các biểu đồ về các tài nguyên CDK được kết nối của bạn, và cung cấp hai plugin:

CDK Graph Diagram Plugin tạo ra các sơ đồ kiến trúc AWS từ cơ sở hạ tầng CDK của bạn.

Để có một cách tiếp cận xác định tương tự, một lựa chọn khả thi là CDK-Dia.

Với những tiến bộ trong Generative AI, nhiều mô hình nền tảng có khả năng tạo ra các sơ đồ chất lượng cao từ cơ sở hạ tầng CDK của bạn. Chúng tôi khuyên bạn nên thử AWS Diagram MCP Server. Xem bài viết blog này để biết hướng dẫn chi tiết.

CDK Graph Threat Composer Plugin tạo ra một Threat Composer mô hình mối đe dọa khởi đầu từ mã CDK của bạn.

Plugin này hoạt động bằng cách đơn giản là lọc một mô hình mối đe dọa cơ sở chứa các mối đe dọa ví dụ, và lọc chúng dựa trên các tài nguyên mà stack của bạn sử dụng.

Nếu bạn quan tâm đến các mối đe dọa ví dụ cụ thể này, bạn có thể sao chép và lọc mô hình mối đe dọa cơ sở, hoặc sử dụng nó làm ngữ cảnh để giúp một mô hình nền tảng tạo ra một mô hình tương tự.

AWS Arch cung cấp các ánh xạ giữa các tài nguyên CloudFormation và các biểu tượng kiến trúc liên quan của chúng cho CDK Graph ở trên.

Tham khảo trang AWS Architecture Icons để biết các tài nguyên liên quan đến biểu tượng. Diagrams cũng cung cấp một cách để xây dựng sơ đồ dưới dạng mã.

Nếu bạn đang sử dụng trực tiếp dự án này, hãy cân nhắc fork dự án và tự quản lý nó!

PDK đã cung cấp một PDKPipelineProject để thiết lập một dự án cơ sở hạ tầng CDK và sử dụng một construct CDK bao bọc một số tài nguyên CDK Pipelines.

Để di chuyển từ cách này, bạn có thể sử dụng trực tiếp các construct CDK Pipelines. Tuy nhiên trên thực tế, có lẽ sẽ đơn giản hơn khi sử dụng các công cụ như GitHub actions hoặc GitLab CI/CD, nơi bạn định nghĩa các CDK Stages và chạy lệnh deploy cho stage phù hợp một cách trực tiếp.

PDK Nag bao bọc CDK Nag, và cung cấp một bộ quy tắc cụ thể để xây dựng các prototype.

Để di chuyển từ PDK Nag, hãy sử dụng CDK Nag trực tiếp. Nếu bạn cần cùng một bộ quy tắc, bạn có thể tạo một “pack” của riêng mình bằng cách làm theo tài liệu tại đây.

Các component được sử dụng phổ biến nhất từ Type Safe API đã được đề cập trong ví dụ di chuyển ở trên, tuy nhiên còn có các tính năng khác mà chi tiết di chuyển được nêu dưới đây.

Nx Plugin for AWS hỗ trợ các API được mô hình hóa bằng Smithy, nhưng không hỗ trợ những API được mô hình hóa trực tiếp bằng OpenAPI. Generator ts#smithy-api là một điểm khởi đầu tốt mà bạn có thể sau đó chỉnh sửa. Bạn có thể định nghĩa đặc tả OpenAPI của mình trong thư mục src của project model thay vì Smithy, và chỉnh sửa build.Dockerfile để sử dụng công cụ sinh code mong muốn cho clients/servers nếu chúng không có sẵn trên NPM. Nếu các công cụ mong muốn của bạn có trên NPM, bạn chỉ cần cài đặt chúng như dev dependencies vào Nx workspace của bạn và gọi chúng trực tiếp như các build target của Nx.

Đối với các backend type-safe được mô hình hóa bằng OpenAPI, bạn có thể cân nhắc sử dụng một trong các OpenAPI Generator Server Generators. Những công cụ này sẽ không sinh code trực tiếp cho AWS Lambda, nhưng bạn có thể sử dụng AWS Lambda Web Adapter để bắc cầu cho nhiều trong số chúng.

Đối với TypeScript clients, bạn có thể sử dụng generator ts#react-websitegenerator api-connection với một ví dụ ts#smithy-api để xem cách clients được sinh ra và tích hợp với một website. Điều này cấu hình các build target sinh clients bằng cách gọi các generator open-api#ts-client hoặc open-api#ts-hooks của chúng tôi. Bạn có thể tự sử dụng các generator này bằng cách trỏ chúng đến OpenAPI Specification của bạn.

Đối với các ngôn ngữ khác, bạn cũng có thể xem liệu có generator nào từ OpenAPI Generator phù hợp với nhu cầu của bạn không.

Bạn cũng có thể xây dựng một generator tùy chỉnh bằng cách sử dụng generator ts#nx-generator. Tham khảo tài liệu của generator đó để biết chi tiết về cách sinh code từ OpenAPI. Bạn có thể sử dụng các template từ Nx Plugin for AWS như một điểm khởi đầu. Bạn thậm chí có thể tham khảo các template từ codebase PDK để có thêm ý tưởng, lưu ý rằng cấu trúc dữ liệu mà các template hoạt động trên đó hơi khác so với Nx Plugin for AWS.

Đối với TypeSpec, phần OpenAPI ở trên cũng áp dụng tương tự. Bạn có thể bắt đầu bằng cách sinh một ts#smithy-api, cài đặt TypeSpec compiler và các OpenAPI packages vào Nx workspace của bạn, và cập nhật target compile của model project để chạy tsp compile thay thế, đảm bảo nó xuất ra một đặc tả OpenAPI vào thư mục dist.

Cách tiếp cận được khuyến nghị là sử dụng TypeSpec HTTP Server generator for JavaScript để sinh code server của bạn, vì điều này hoạt động trực tiếp trên mô hình TypeSpec của bạn.

Bạn có thể sử dụng AWS Lambda Web Adapter để chạy server được sinh ra trên AWS Lambda.

Bạn cũng có thể sử dụng bất kỳ tùy chọn OpenAPI nào ở trên.

TypeSpec có các code generator riêng cho clients trong cả ba ngôn ngữ được Type Safe API hỗ trợ:

Phần OpenAPI ở trên cũng áp dụng vì TypeSpec có thể compile sang OpenAPI.

Ví dụ di chuyển ở trên phác thảo việc di chuyển để sử dụng generator ts#smithy-api. Phần này đề cập đến các tùy chọn cho Python và Java backends và clients.

Smithy code generator for Java. Công cụ này có Java server generator cũng như một adapter để chạy Java server được sinh ra trên AWS Lambda.

Smithy không có server generator cho Python, vì vậy bạn sẽ cần đi qua OpenAPI. Tham khảo phần trên về API được Mô hình hóa với OpenAPI để biết các tùy chọn tiềm năng.

Smithy code generator for Java. Công cụ này có Java client generator.

Đối với Python clients, bạn có thể xem Smithy Python.

Đối với TypeScript, xem Smithy TypeScript, hoặc sử dụng cách tiếp cận tương tự mà chúng tôi đã thực hiện trong ts#smithy-api bằng cách đi qua OpenAPI (chúng tôi đã chọn cách này vì nó mang lại sự nhất quán giữa các API tRPC, FastAPI và Smithy thông qua TanStack Query hooks).

Type Safe API đã cung cấp một loại Projen project có tên SmithyShapeLibraryProject để cấu hình một project chứa các mô hình Smithy có thể được tái sử dụng bởi nhiều API dựa trên Smithy.

Cách đơn giản nhất để đạt được điều này là làm như sau:

  1. Tạo shape library của bạn bằng generator smithy#project:

    1. Install the Nx Console VSCode Plugin if you haven't already
    2. Open the Nx Console in VSCode
    3. Click Generate (UI) in the "Common Nx Commands" section
    4. Search for @aws/nx-plugin - smithy#project
    5. Fill in the required parameters
      • Click Generate

      Chỉ định bất kỳ tên nào cho tùy chọn serviceName, vì chúng ta sẽ xóa shape service.

    6. Thay thế mô hình mặc định trong src bằng các shape bạn muốn định nghĩa

    7. Cập nhật smithy-build.json để xóa plugins và bất kỳ maven dependencies không sử dụng nào

    8. Thay thế build.Dockerfile bằng các bước build tối thiểu:

      build.Dockerfile
      FROM public.ecr.aws/docker/library/node:24 AS builder
      # Output directory
      RUN mkdir /out
      # Install 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 project files
      COPY smithy-build.json .
      COPY src src
      # Smithy build with Maven cache mount
      RUN --mount=type=cache,target=/root/.m2/repository,id=maven-cache \
      smithy build
      RUN cp -r build/* /out/
      # Export the /out directory
      FROM scratch AS export
      COPY --from=builder /out /

    Trong (các) service model project của bạn, thực hiện các thay đổi sau để sử dụng shape library:

    1. Cập nhật target compile trong project.json để thêm workspace làm build context, và một dependency vào target build của shape library

      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. Cập nhật build.Dockerfile để sao chép thư mục src từ shape library của bạn. Ví dụ, giả sử shape library nằm trong packages/shapes:

      build.Dockerfile
      # Copy project files
      COPY smithy-build.json .
      COPY src src
      COPY --from=workspace packages/shapes/src shapes
    3. Cập nhật smithy-build.json để thêm thư mục shapes vào sources:

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

    Type Safe API đã cung cấp các interceptor mặc định sau:

    • Logging, tracing và metrics interceptors sử dụng Powertools for AWS Lambda
    • Try-catch interceptor để xử lý các exception chưa được bắt
    • CORS interceptor để trả về CORS headers

    Generator ts#smithy-api tích hợp logging, tracing và metrics với Powertools for AWS Lambda sử dụng Middy. Hành vi của try-catch interceptor được tích hợp sẵn trong Smithy TypeScript SSDK, và CORS headers được thêm vào trong handler.ts.

    Đối với logging, tracing và metrics interceptors trong bất kỳ ngôn ngữ nào, sử dụng Powertools for AWS Lambda trực tiếp.

    Để di chuyển các custom interceptors, chúng tôi khuyến nghị sử dụng các thư viện sau:

    Type Safe API đã cung cấp sinh tài liệu sử dụng Redocly CLI. Điều này rất dễ thêm vào một project hiện có sau khi bạn đã di chuyển nó như trên.

    1. Cài đặt Redocly CLI

      Terminal window
      pnpm add -Dw @redocly/cli
    2. Thêm một target sinh tài liệu vào project model của bạn sử dụng redocly build-docs, ví dụ:

      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"]
      }
      }

    Bạn cũng có thể cân nhắc các OpenAPI Generator documentation generators.

    Type Safe API đã sinh các mock cho bạn trong infrastructure package được sinh ra của nó.

    Bạn có thể chuyển sang JSON Schema Faker có thể tạo dữ liệu mock dựa trên JSON Schemas. Công cụ này có thể hoạt động trực tiếp trên một đặc tả OpenAPI, và có một CLI mà bạn có thể chạy như một phần của quá trình build project model của bạn.

    Bạn có thể cập nhật CDK infrastructure của mình để đọc file JSON được xuất ra bởi JSON Schema Faker, và trả về API Gateway MockIntegration phù hợp cho một integration, dựa trên metadata.gen.ts được sinh ra (giả sử bạn đã sử dụng generator ts#smithy-api).

    Type Safe API đã hỗ trợ triển khai API với sự kết hợp của các ngôn ngữ khác nhau trong backend. Điều này cũng có thể đạt được bằng cách cung cấp “overrides” cho các integration khi khởi tạo API construct của bạn trong CDK:

    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(),
    });

    Bạn sẽ cần “stub” service/router của mình để service của bạn có thể compile nếu sử dụng ts#smithy-api và TypeScript Server SDK, ví dụ:

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

    Type Safe API đã thêm xác thực API Gateway native cho request bodies dựa trên đặc tả OpenAPI của bạn vì nó sử dụng construct SpecRestApi bên dưới.

    Với generator ts#smithy-api, xác thực được thực hiện bởi chính Server SDK. Điều này tương tự đối với hầu hết các server generator.

    Nếu bạn muốn triển khai xác thực API Gateway native, bạn có thể làm như vậy bằng cách chỉnh sửa packages/common/constructs/src/core/api/rest-api.ts để đọc JSON schema liên quan cho request body của mỗi operation từ đặc tả OpenAPI của bạn.

    Thật không may, không có con đường di chuyển đơn giản nào cho websocket API của Type Safe API sử dụng API Gateway và Lambda với phát triển API dựa trên mô hình. Tuy nhiên, phần này của hướng dẫn nhằm mục đích ít nhất cung cấp một vài ý tưởng.

    Cân nhắc sử dụng AsyncAPI để mô hình hóa API của bạn thay vì OpenAPI hoặc TypeSpec vì điều này được thiết kế để xử lý các API bất đồng bộ. AsyncAPI NodeJS Template có thể sinh một Node websocket backend mà bạn có thể host trên ECS chẳng hạn.

    Bạn cũng có thể cân nhắc AppSync Events cho infrastructure, và sử dụng Powertools. Bài đăng blog này đáng đọc!

    Một tùy chọn khác là sử dụng GraphQL APIs với websockets trên AppSync, mà chúng tôi có một GitHub issue mà bạn có thể +1! Tham khảo hướng dẫn dành cho nhà phát triển AppSync để biết chi tiết và liên kết đến các dự án mẫu.

    Bạn cũng có thể cân nhắc tự xây dựng các code generator của riêng mình để diễn giải các vendor extension giống như Type Safe API. Tham khảo phần API được Mô hình hóa với OpenAPI để biết chi tiết về việc xây dựng các code generator tùy chỉnh dựa trên OpenAPI. Bạn có thể tìm thấy các template mà Type Safe API sử dụng cho API Gateway Websocket API Lambda handlers tại đây, và client tại đây.

    Bạn cũng có thể cân nhắc di chuyển để sử dụng generator ts#trpc-api để sử dụng tRPC. Tại thời điểm viết bài, chúng tôi chưa có hỗ trợ cho subscriptions/streaming nhưng nếu đây là điều bạn cần, hãy thêm +1 vào GitHub issue theo dõi điều này.

    Smithy là protocol agnostic, nhưng chưa có hỗ trợ cho giao thức Websocket, tham khảo GitHub issue này theo dõi hỗ trợ.

    PDK hỗ trợ cơ sở hạ tầng CDK được viết bằng Python và Java. Chúng tôi không hỗ trợ điều này trong Nx Plugin for AWS tại thời điểm viết bài.

    Hướng đi được khuyến nghị là di chuyển cơ sở hạ tầng CDK của bạn sang TypeScript, hoặc sử dụng các generator của chúng tôi và di chuyển gói common constructs sang ngôn ngữ mong muốn của bạn. Bạn có thể sử dụng Generative AI để tăng tốc các loại di chuyển này, ví dụ như Amazon Q CLI. Bạn có thể để một AI agent lặp lại quá trình di chuyển cho đến khi các template CloudFormation được tổng hợp giống hệt nhau.

    Điều tương tự áp dụng cho cơ sở hạ tầng được tạo ra bởi Type Safe API trong Python hoặc Java - bạn có thể dịch construct rest-api.ts chung từ gói common constructs, và triển khai metadata generator đơn giản của riêng bạn cho ngôn ngữ đích (tham khảo phần APIs Modelled with OpenAPI).

    Bạn có thể sử dụng generator py#project cho một dự án Python cơ bản để thêm mã CDK của bạn vào (và di chuyển file cdk.json của bạn, thêm các target liên quan). Bạn có thể sử dụng plugin @nx/gradle của Nx cho các dự án Java, hoặc @jnxplus/nx-maven cho Maven.

    PDK được xây dựng dựa trên Projen. Projen và Nx Generators có những khác biệt khá cơ bản, điều này có nghĩa là mặc dù về mặt kỹ thuật có thể kết hợp chúng nhưng rất có thể đây là một anti-pattern. Projen quản lý các file dự án dưới dạng code sao cho chúng không thể được chỉnh sửa trực tiếp, trong khi Nx generators tạo ra các file dự án một lần và sau đó code có thể được tự do chỉnh sửa.

    Nếu bạn muốn tiếp tục sử dụng Projen, bạn có thể tự triển khai các loại dự án Projen mong muốn của mình. Để tuân theo các pattern từ Nx Plugin for AWS, bạn có thể chạy các generators của chúng tôi hoặc xem xét mã nguồn của chúng trên GitHub để thấy cách các loại dự án mong muốn của bạn được xây dựng, và triển khai các phần liên quan bằng cách sử dụng các primitives của Projen.