Smithy API đến Cơ sở dữ liệu Quan hệ
Generator connection kết nối một Smithy API với một dự án Cơ sở dữ liệu Quan hệ, tiêm một Prisma client vào service context để tất cả các triển khai operation có thể truy cập cơ sở dữ liệu.
Điều kiện tiên quyết
Phần tiêu đề “Điều kiện tiên quyết”Trước khi sử dụng generator này, hãy đảm bảo bạn có:
- Một dự án Smithy TypeScript API (được tạo bằng
ts#apivới--framework=smithy) - Một dự án
ts#rdb
Cách sử dụng
Phần tiêu đề “Cách sử dụng”Chạy Generator
Phần tiêu đề “Chạy Generator”pnpm nx g @aws/nx-plugin:connectionyarn nx g @aws/nx-plugin:connectionnpx nx g @aws/nx-plugin:connectionbunx nx g @aws/nx-plugin:connectionBạn cũng có thể thực hiện chạy thử để xem những tệp nào sẽ bị thay đổi
pnpm nx g @aws/nx-plugin:connection --dry-runyarn nx g @aws/nx-plugin:connection --dry-runnpx nx g @aws/nx-plugin:connection --dry-runbunx nx g @aws/nx-plugin:connection --dry-run- Cài đặt Nx Console VSCode Plugin nếu bạn chưa cài đặt
- Mở Nx Console trong VSCode
- Nhấp
Generate (UI)trong phần "Common Nx Commands" - Tìm kiếm
@aws/nx-plugin - connection - Điền các tham số bắt buộc
- Nhấp
Generate
Chọn dự án backend Smithy API của bạn làm nguồn và dự án cơ sở dữ liệu quan hệ của bạn làm đích.
Tùy chọn
Phần tiêu đề “Tùy chọn”| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
| sourceProject Bắt buộc | string | - | Dự án nguồn |
| targetProject Bắt buộc | string | - | Dự án đích để kết nối tới |
| sourceComponent | string | - | Component nguồn để kết nối từ đó (tên component, đường dẫn tương đối so với thư mục gốc của dự án nguồn, hoặc generator id). Sử dụng '.' để chọn rõ ràng dự án làm nguồn. |
| targetComponent | string | - | Component đích để kết nối tới (tên component, đường dẫn tương đối so với thư mục gốc của dự án đích, hoặc generator id). Sử dụng '.' để chọn rõ ràng dự án làm đích. |
| preferInstallDependencies | boolean | true | Có nên cài đặt các dependencies sau khi generator chạy hay không. Đặt thành false để trì 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. |
Kết quả của Generator
Phần tiêu đề “Kết quả của Generator”Generator sửa đổi ba tệp hiện có trong backend Smithy API của bạn:
Thư mụcpackages/api/src
- context.ts thuộc tính
dbđược thêm vàoServiceContext - handler.ts Prisma client được tạo bên trong
lambdaHandler, được truyền đếnserviceHandler.handle - local-server.ts Prisma client được tạo bên trong request handler, được truyền đến
serviceHandler.handle
- context.ts thuộc tính
Ngoài ra, nó cập nhật target dev của API để tự động khởi động cơ sở dữ liệu.
Cách hoạt động
Phần tiêu đề “Cách hoạt động”ServiceContext
Phần tiêu đề “ServiceContext”Generator thêm một thuộc tính db có kiểu vào ServiceContext trong context.ts:
import { getPrisma as getMyDb } from '@my-scope/my-db';
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>;}Lambda Handler
Phần tiêu đề “Lambda Handler”Prisma client được khởi tạo bên trong lambdaHandler và được truyền qua service context:
import { getPrisma as getMyDb } from '@my-scope/my-db';
export const lambdaHandler = async (event: APIGatewayProxyEvent) => { const httpRequest = convertEvent(event); const myDb = await getMyDb(); const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, }); return convertVersion1Response(httpResponse);};Sử dụng Cơ sở dữ liệu trong Operations
Phần tiêu đề “Sử dụng Cơ sở dữ liệu trong Operations”Truy cập db từ context trong các triển khai operation của bạn:
import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js';
export const listUsers = async ( input: ListUsersOperationInput, ctx: ServiceContext,): Promise<ListUsersOperationOutput> => { const users = await ctx.myDb.user.findMany(); return { users };};Nhiều Cơ sở dữ liệu
Phần tiêu đề “Nhiều Cơ sở dữ liệu”Chạy generator lại với một target khác sẽ thêm cơ sở dữ liệu thứ hai cùng với cơ sở dữ liệu đầu tiên. Cả hai client đều được thêm vào ServiceContext và được khởi tạo trong handler.ts:
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>; otherDb: Awaited<ReturnType<typeof getOtherDb>>;}const myDb = await getMyDb();const otherDb = await getOtherDb();const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, otherDb,});Cơ sở hạ tầng
Phần tiêu đề “Cơ sở hạ tầng”Để cho phép API của bạn kết nối với cơ sở dữ liệu tại runtime, các hàm Lambda API phải được triển khai vào cùng VPC với cơ sở dữ liệu và được cấp quyền truy cập mạng và IAM.
Trong application stack của bạn, triển khai API vào cùng VPC với cơ sở dữ liệu, sau đó gọi allowDefaultPortFrom và grantConnect để mở đường dẫn mạng và cấp quyền IAM rds-db:connect cho mỗi Lambda handler:
import { MyDatabase } from '@my-scope/common-constructs';
const db = new MyDatabase(this, 'Db', { vpc, ... });
const api = new MyApi(this, 'Api', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS }, }) .build(),});
Object.entries(api.integrations).forEach(([operation, integration]) => { db.allowDefaultPortFrom(integration.handler, `Allow ${operation} to connect to the database`); db.grantConnect(integration.handler);});Triển khai các hàm Lambda API vào private subnet with egress, không phải private isolated subnet. Tại runtime, getPrisma() lấy thông tin chi tiết kết nối cơ sở dữ liệu từ AWS AppConfig, đây là một public AWS service endpoint yêu cầu quyền truy cập internet đi ra.
Triển khai API vào cùng VPC với cơ sở dữ liệu, cấp cho nó quyền rds-db:connect thông qua additional_iam_policy_statements, và mở đường dẫn mạng bằng một cặp security group rules. Các tài nguyên aws_vpc.main và aws_subnet được định nghĩa trong hướng dẫn triển khai cơ sở dữ liệu:
module "my_database" { source = "../../common/terraform/src/app/dbs/my-database" vpc_id = aws_vpc.main.id database_subnet_ids = aws_subnet.database[*].id lambda_subnet_ids = aws_subnet.private[*].id}
module "api" { source = "../../common/terraform/src/app/apis/my-api" enable_vpc = true vpc_id = aws_vpc.main.id subnet_ids = aws_subnet.private[*].id
appconfig_application_id = module.runtime_config_appconfig.application_id appconfig_application_arn = module.runtime_config_appconfig.application_arn
additional_iam_policy_statements = [ { Effect = "Allow" Action = ["rds-db:connect"] Resource = [ "arn:aws:rds-db:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:dbuser:${module.my_database.connect_resource_id}/${module.my_database.database_runtime_user}" ] } ]}
resource "aws_vpc_security_group_ingress_rule" "api_to_database" { description = "Allow the API Lambda functions to connect to the database" security_group_id = module.my_database.security_group_id referenced_security_group_id = module.api.security_group_id from_port = module.my_database.cluster_port to_port = module.my_database.cluster_port ip_protocol = "tcp"}
resource "aws_vpc_security_group_egress_rule" "api_to_database" { description = "Allow outbound traffic from the API Lambda functions to the database" security_group_id = module.api.security_group_id referenced_security_group_id = module.my_database.security_group_id from_port = module.my_database.cluster_port to_port = module.my_database.cluster_port ip_protocol = "tcp"}Triển khai các hàm Lambda API vào private subnets with egress, không phải private isolated subnets. appconfig_application_id/appconfig_application_arn đến từ runtime configuration AppConfig application được chia sẻ được khai báo một lần trong root module của bạn, không phải từ database module — việc truyền chúng sẽ đặt RUNTIME_CONFIG_APP_ID trên các hàm Lambda và cấp cho chúng quyền đọc vào application. Bao gồm namespace database khi khởi tạo nó để runtime configuration entry của database module được triển khai:
module "runtime_config_appconfig" { source = "../../common/terraform/src/core/runtime-config/appconfig"
application_name = "my-app-runtime-config" namespaces = ["connection", "agentcore", "database"]}Yêu cầu SSL Khi Kết nối Không có RDS Proxy
Phần tiêu đề “Yêu cầu SSL Khi Kết nối Không có RDS Proxy”Đối với các kết nối trực tiếp đến Aurora cluster từ Lambda runtime Node.js 20 trở lên, hãy tải gói CA của Amazon RDS bằng cách thiết lập NODE_EXTRA_CA_CERTS:
const api = new Api(this, 'Api', { integrations: Api.defaultIntegrations(this) .withDefaultOptions({ environment: { NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem', }, }) .build(),});module "api" { source = "..." ...
environment_variables = { NODE_EXTRA_CA_CERTS = "/var/runtime/ca-cert.pem" }}Để biết thêm chi tiết, hãy xem yêu cầu SSL/TLS cho kết nối Amazon RDS của AWS Lambda và tài liệu TLS của Amazon RDS Proxy. Khi sử dụng RDS Proxy, bạn không cần cấu hình gói CA của RDS trong Lambda function của mình.
Phát triển Cục bộ
Phần tiêu đề “Phát triển Cục bộ”Generator áp dụng cùng một cách tiêm Prisma client bên trong request handler trong local-server.ts:
import { getPrisma as getMyDb } from '@my-scope/my-db';
const server = createServer(async function (req, res) { const httpRequest = convertRequest(req); const myDb = await getMyDb(); const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, }); return writeResponse(httpResponse, res);});pnpm nx dev <api-project-name>yarn nx dev <api-project-name>npx nx dev <api-project-name>bunx nx dev <api-project-name>Điều này khởi động cả API và cơ sở dữ liệu cục bộ. Biến môi trường LOCAL_DEV=true được đặt tự động, vì vậy Prisma client kết nối với cơ sở dữ liệu Docker cục bộ thay vì Aurora.