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

TypeScript DynamoDB

Generator này tạo một dự án TypeScript DynamoDB mới được hỗ trợ bởi Amazon DynamoDB, sử dụng ElectroDB để mô hình hóa thực thể an toàn kiểu. Nó tạo ra mã ứng dụng và cơ sở hạ tầng cần thiết để cung cấp và quản lý bảng DynamoDB bằng AWS CDK hoặc Terraform, với hỗ trợ thiết kế bảng đơn và phát triển cục bộ tích hợp sẵn thông qua DynamoDB Local.

Chạy generator này@aws/nx-plugin:ts#dynamodb

pnpm nx g @aws/nx-plugin:ts#dynamodb
Xây dựng lệnh của bạn8

Bắt buộc

Tùy chọn của generator8 tùy chọn
nameBắt buộcstring

Tên của dự án DynamoDB cần tạo

directorystringMặc định: packages

Thư mục để lưu trữ dự án.

frameworkenumMặc định: electrodb

Framework sử dụng cho các entity DynamoDB.

electrodb
infraenumMặc định: dynamodb

Cơ sở hạ tầng để cung cấp cho bảng DynamoDB.

dynamodbnone
iacenumMặc định: inherit

Nhà cung cấp IaC ưa thích. Mặc định giá trị này được kế thừa từ lựa chọn ban đầu của bạn.

inheritcdkterraform
subDirectorystring

Thư mục con mà dự án được đặt trong đó. Mặc định là tên dự án.

tableNamestring

Tên bảng DynamoDB. Tự động tạo nếu không chỉ định.

preferInstallDependenciesbooleanMặc định: true

Có nên cài đặt các dependencies sau khi generator chạy hay không. Đặt thành false để hoãn việc cài đặt khi chạy nhiều generator liên tiếp (việc cài đặt vẫn sẽ chạy nếu cần thiết để các generator tiếp theo có thể tính toán Nx project graph); cài đặt một lần vào cuối.

Generator tạo cấu trúc dự án sau trong thư mục <directory>/<name>:

  • Thư mụcsrc
    • index.ts Project entry point and exports
    • client.ts DynamoDB client singleton and table name resolution
    • Thư mụcentities
      • example.ts Example ElectroDB entity definition
      • index.ts Entity exports
  • config.json Table configuration including GSI definitions and local development settings
  • package.json Project manifest defining the project’s package name and dependencies
  • project.json Project configuration and build targets

Các script phát triển cục bộ được chia sẻ trên tất cả các dự án DynamoDB (cả TypeScript và Python) và được tạo một lần vào:

  • Thư mụcpackages/common/scripts/src/dynamodb
    • create-local-table.ts Creates the DynamoDB table in the local DynamoDB Local instance
    • pull-image.ts Pulls the DynamoDB Local image
    • start-container.ts Starts the DynamoDB Local container

Vì generator này cung cấp infrastructure as code dựa trên iac bạn đã chọn, nó sẽ tạo một dự án trong packages/common bao gồm các CDK constructs hoặc Terraform modules liên quan.

Dự án infrastructure as code chung được cấu trúc như sau:

  • Thư mụcpackages/common/constructs
    • Thư mụcsrc
      • Thư mụcapp/ Constructs for infrastructure specific to a project/generator
      • Thư mụccore/ Generic constructs which are reused by constructs in app
      • index.ts Entry point exporting constructs from app
    • project.json Project build targets and configuration
  • Thư mụcpackages/common/constructs/src
    • Thư mụcapp
      • Thư mụcdynamodb
        • <name>.ts Cơ sở hạ tầng cụ thể cho bảng của bạn
    • Thư mụccore
      • dynamodb.ts Construct bảng DynamoDB chung

Dự án được triển khai cung cấp chính bảng đó, mà bất kỳ dự án nào được kết nối với nó đều đọc và ghi:

Loading the diagram…

Generator cấu hình một target dev để khởi động một instance DynamoDB Local và tạo bảng. Sử dụng target dev của dự án:

Terminal window
pnpm nx dev <project-name>

Điều này tự động:

  1. Kéo image DynamoDB Local (target pull-image)
  2. Khởi động một container
  3. Tạo một bảng local với các index được định nghĩa trong config.json

Dự án được tạo sử dụng ElectroDB để mô hình hóa thực thể an toàn kiểu trên một bảng DynamoDB đơn, tuân theo thiết kế bảng đơn của DynamoDB. Thêm hoặc cập nhật các tệp thực thể trong src/entities/, sử dụng thực thể ví dụ được tạo làm điểm khởi đầu.

Định nghĩa thực thể ví dụ:

packages/my-table/src/entities/example.ts
import { Entity } from 'electrodb';
import { getDynamoDBClient, resolveTableName } from '../client.js';
export const createExampleEntity = async () =>
new Entity(
{
model: {
entity: 'example',
version: '1',
service: 'MyTable',
},
attributes: {
id: {
type: 'string',
required: true,
},
createdAt: {
type: 'string',
required: true,
default: () => new Date().toISOString(),
readOnly: true,
},
updatedAt: {
type: 'string',
required: true,
default: () => new Date().toISOString(),
watch: '*',
set: () => new Date().toISOString(),
},
},
indexes: {
primary: {
pk: {
field: 'pk',
composite: ['id'],
},
sk: {
field: 'sk',
composite: [],
},
},
},
},
{ client: getDynamoDBClient(), table: await resolveTableName() },
);

Để biết thêm chi tiết, xem tài liệu thực thể ElectroDB.

src/client.ts được tạo xuất hai tiện ích chính:

  • getDynamoDBClient() — trả về một DynamoDBClient singleton được lưu trong bộ nhớ cache. Khi LOCAL_DEV=true, kết nối đến phiên bản DynamoDB Local cục bộ; nếu không tạo một AWS client sử dụng chuỗi thông tin xác thực mặc định.
  • resolveTableName() — trả về tên bảng DynamoDB. Khi LOCAL_DEV=true, trả về hằng số tên bảng cục bộ; nếu không lấy tên từ AWS AppConfig sử dụng biến môi trường RUNTIME_CONFIG_APP_ID và lưu vào bộ nhớ cache cho các lần gọi tiếp theo.

Việc dừng dev (ví dụ: bằng Ctrl+C) sẽ tự động xóa container DynamoDB Local, nhưng vẫn giữ lại named volume để dữ liệu của bạn được bảo toàn qua các lần khởi động lại.

GSI được định nghĩa trong config.json ở thư mục gốc dự án dưới khóa tableConfig.globalSecondaryIndexes. Thêm một mục cho mỗi GSI, tuân theo quy ước đặt tên khóa GSI thiết kế bảng đơn:

config.json
{
...
"tableConfig": {
"globalSecondaryIndexes": [
{
"indexName": "gsi1pk-gsi1sk-index",
"partitionKey": "gsi1pk",
"sortKey": "gsi1sk"
},
{
"indexName": "gsi2pk-gsi2sk-index",
"partitionKey": "gsi2pk",
"sortKey": "gsi2sk"
}
]
}
}

Trường sortKey là tùy chọn cho các GSI chỉ có hash-key.

File cấu hình này là nguồn sự thật duy nhất được đọc bởi tất cả các consumer:

  • Phát triển cục bộdev đọc config.json và tạo hoặc cập nhật bảng cục bộ để khớp với danh sách GSI
  • CDK — construct đọc config.json tại thời điểm synth, do đó các thay đổi GSI được phản ánh trong lần cdk deploy tiếp theo
  • Terraform — module đọc config.json tại thời điểm plan/apply

Trong bất kỳ dự án TypeScript nào, import các factory thực thể từ gói DynamoDB của bạn và sử dụng chúng trực tiếp:

import { createExampleEntity } from '@my-scope/my-table';
const entity = await createExampleEntity();
const result = await entity.query.primary({ id: '123' }).go();

Ở hậu trường, createExampleEntity() gọi resolveTableName() để lấy tên bảng từ AWS AppConfig tại thời điểm chạy.

Generator DynamoDB tạo cơ sở hạ tầng CDK hoặc Terraform dựa trên iac bạn đã chọn.

Construct CDK được tạo trong common/constructs. Ví dụ sử dụng:

packages/infra/src/stacks/application-stack.ts
import { MyTable } from '@my-scope/common-constructs';
export class ApplicationStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const table = new MyTable(this, 'Table');
}
}

Điều này cung cấp một bảng DynamoDB với:

  • pk (partition key) và sk (sort key), cả hai đều là kiểu String
  • Global Secondary Indexes như được định nghĩa trong config.json
  • Thanh toán theo yêu cầu (PAY_PER_REQUEST)
  • Mã hóa KMS do khách hàng quản lý với tự động xoay vòng khóa
  • Khôi phục theo thời điểm được bật
  • Bảo vệ xóa được bật
  • Tên bảng được đăng ký trong Runtime Config dưới namespace dynamodb trong AWS AppConfig

Bảng được bảo vệ bởi hai biện pháp bảo vệ độc lập, do đó việc tắt một trong hai không thể xóa dữ liệu của bạn:

  • deletionProtection, được thực thi bởi DynamoDB.
  • RemovalPolicy.RETAIN, được thực thi bởi CloudFormation, giữ bảng tại chỗ khi nó bị xóa khỏi stack.

Vô hiệu hóa bảo vệ cho các môi trường mà việc xóa bảng được mong đợi, chẳng hạn như các stack phát triển hoặc xem trước có thời gian tồn tại ngắn.

packages/infra/src/stacks/application-stack.ts
import { RemovalPolicy } from 'aws-cdk-lib';
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
deletionProtection: false,
removalPolicy: RemovalPolicy.DESTROY,
});

Bảng mặc định sử dụng thanh toán theo yêu cầu (PAY_PER_REQUEST). Chuyển sang dung lượng được cung cấp cho các khối lượng công việc có thông lượng cao và có thể dự đoán.

packages/infra/src/stacks/application-stack.ts
import { BillingMode } from 'aws-cdk-lib/aws-dynamodb';
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
billingMode: BillingMode.PROVISIONED,
readCapacity: 5,
writeCapacity: 5,
});

Khôi phục theo thời điểm được bật mặc định, cho phép bạn khôi phục bảng về bất kỳ thời điểm nào trong 35 ngày qua.

packages/infra/src/stacks/application-stack.ts
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: false },
});

Bảng được mã hóa với khóa KMS do khách hàng quản lý theo mặc định, được tạo tự động cho bạn. Chuyển sang khóa do AWS quản lý, khóa thuộc sở hữu của AWS, hoặc mang khóa KMS của riêng bạn, nếu bạn quản lý mã hóa theo cách khác.

Sử dụng khóa KMS aws/dynamodb được chia sẻ mà AWS quản lý thay mặt bạn. Nó hiển thị trong bảng điều khiển KMS của tài khoản bạn và được tính phí theo yêu cầu, nhưng không có khóa nào để bạn tạo, xoay vòng hoặc xóa.

packages/infra/src/stacks/application-stack.ts
import { TableEncryption } from 'aws-cdk-lib/aws-dynamodb';
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
encryption: TableEncryption.AWS_MANAGED,
});

Sử dụng một khóa hoàn toàn thuộc sở hữu và được quản lý bởi AWS — miễn phí, không có khóa nào hiển thị trong tài khoản của bạn. Tùy chọn đơn giản nhất khi bạn không cần khóa hiển thị cho khách hàng hoặc tài khoản vì lý do tuân thủ.

packages/infra/src/stacks/application-stack.ts
import { TableEncryption } from 'aws-cdk-lib/aws-dynamodb';
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
encryption: TableEncryption.DEFAULT,
});
iac = terraform

Trên bảng đã triển khai, việc thay đổi encryption khỏi CUSTOMER_MANAGED (sang AWS_MANAGED hoặc DEFAULT) trong một lệnh terraform apply duy nhất sẽ thất bại: Terraform phá hủy khóa do khách hàng quản lý trước khi cập nhật bảng, và DynamoDB sau đó từ chối cập nhật vì khóa đã đang chờ xóa.

Giải quyết bằng cách cập nhật mã hóa của bảng trực tiếp qua AWS CLI trước, sau đó để Terraform bắt kịp và dọn dẹp khóa bị bỏ rơi:

Terminal window
# For AWS_MANAGED:
aws dynamodb update-table --table-name <table-name> \
--sse-specification Enabled=true,SSEType=KMS,KMSMasterKeyId=alias/aws/dynamodb
# For DEFAULT:
aws dynamodb update-table --table-name <table-name> --sse-specification Enabled=false
# Then wait for this to report ENABLED (or for SSEDescription to disappear, for DEFAULT):
aws dynamodb describe-table --table-name <table-name> --query Table.SSEDescription.Status

Sau đó cập nhật encryption trong cấu hình Terraform của bạn và chạy terraform apply như bình thường — Terraform bây giờ chỉ cần phá hủy khóa đã không sử dụng, không còn gì phụ thuộc vào nó.

Cung cấp một khóa do khách hàng quản lý hiện có thay vì để một khóa được tạo cho bạn. Khóa phải đã cấp cho dịch vụ DynamoDB các quyền cần thiết trong chính sách khóa của nó.

packages/infra/src/stacks/application-stack.ts
import { Key } from 'aws-cdk-lib/aws-kms';
import { MyTable } from '@my-scope/common-constructs';
const key = Key.fromKeyArn(this, 'Key', 'arn:aws:kms:us-east-1:111111111111:key/my-key-id');
const table = new MyTable(this, 'Table', {
encryptionKey: key,
});

Khi bảng tạo khóa KMS do khách hàng quản lý của riêng nó (mặc định, và chỉ khi bạn chưa cung cấp khóa của riêng mình), khóa đó có tính năng xoay vòng khóa tự động được bật theo mặc định. Vô hiệu hóa nó nếu chính sách bảo mật của bạn quản lý xoay vòng từ bên ngoài.

packages/infra/src/stacks/application-stack.ts
import { MyTable } from '@my-scope/common-constructs';
const table = new MyTable(this, 'Table', {
enableKeyRotation: false,
});

Sử dụng generator connection để tích hợp dự án này với các dự án khác trong workspace của bạn. Các kết nối sau liên quan đến dự án này:

tRPCAmazon DynamoDB
tRPC API to TypeScript DynamoDBConnect a tRPC API to a DynamoDB table
SmithyAmazon DynamoDB
Smithy API to TypeScript DynamoDBConnect a Smithy API to a DynamoDB table
Strands AgentsTypeScriptAmazon DynamoDB
TypeScript Agent to TypeScript DynamoDBConnect a TypeScript Agent to a DynamoDB table
Model Context ProtocolAmazon DynamoDB
MCP Server to TypeScript DynamoDBConnect a TypeScript MCP Server to a DynamoDB table