tRPC API đến Cơ sở dữ liệu Quan hệ
Trình tạo connection kết nối một tRPC API với dự án Relational Database, tạo ra một plugin middleware tRPC an toàn kiểu giúp cung cấp Prisma client trong ngữ cảnh procedure của bạn.
Đ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 trình tạo này, hãy đảm bảo bạn có:
Cách sử dụng
Phần tiêu đề “Cách sử dụng”Chạy Trình tạo
Phần tiêu đề “Chạy Trình tạo”- 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
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-runChọn dự án tRPC 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 Trình tạo
Phần tiêu đề “Kết quả của Trình tạo”Trình tạo tạo một tệp middleware trong dự án tRPC API của bạn:
Thư mụcpackages/api/src
Thư mụcmiddleware
- <db-name>.ts plugin tRPC cung cấp Prisma client trong ngữ cảnh procedure
Ngoài ra, nó cập nhật target dev của tRPC API để tự động khởi động cơ sở dữ liệu khi chạy cục bộ.
Sử dụng Middleware
Phần tiêu đề “Sử dụng Middleware”Đăng ký Plugin
Phần tiêu đề “Đăng ký Plugin”Thêm plugin được tạo vào tRPC router của bạn để tất cả các procedure sử dụng nó có quyền truy cập vào cơ sở dữ liệu:
import { t } from './init.js';import { createMyDbPlugin } from './middleware/my-db.js';
export const authenticatedProcedure = t.procedure .concat(createMyDbPlugin());Truy cập Cơ sở dữ liệu trong Procedures
Phần tiêu đề “Truy cập Cơ sở dữ liệu trong Procedures”Plugin hợp nhất IMyDbContext vào ngữ cảnh procedure của bạn, làm cho myDb có sẵn như một thuộc tính tùy chọn:
import { z } from 'zod';import { authenticatedProcedure } from '../router.js';
export const listUsers = authenticatedProcedure .output(z.array(z.object({ id: z.string(), name: z.string() }))) .query(async ({ ctx }) => { // ctx.myDb là Prisma client — được định kiểu là Awaited<ReturnType<typeof getPrisma>> return await ctx.myDb!.user.findMany(); });MySQL: Ngắt kết nối Sau Mỗi Request
Phần tiêu đề “MySQL: Ngắt kết nối Sau Mỗi Request”Khi cơ sở dữ liệu đích sử dụng engine MySQL, middleware được tạo bọc opts.next() trong một khối try/finally gọi $disconnect():
return t.procedure.use(async (opts) => { const myDb = await getPrisma(); try { return await opts.next({ ctx: { ...opts.ctx, myDb } }); } finally { await myDb.$disconnect(); }});Điều này giải quyết vấn đề adapter MySQL giữ vòng lặp sự kiện Node.js mở sau một truy vấn, điều này sẽ ngăn Lambda xả các phản hồi streaming. Ngắt kết nối trong finally giải phóng vòng lặp sự kiện để phản hồi có thể hoàn thành. Xem MySQL: API Gateway Streaming Mode để biết chi tiết.
PostgreSQL không yêu cầu điều này — adapter của nó sử dụng connection pool được cấu hình với allowExitOnIdle: true.
Nhiều Cơ sở dữ liệu
Phần tiêu đề “Nhiều Cơ sở dữ liệu”Bạn có thể kết nối thêm các cơ sở dữ liệu bằng cách chạy lại trình tạo với một đích khác. Mỗi cơ sở dữ liệu có plugin và giao diện ngữ cảnh riêng:
export const dbProcedure = t.procedure .concat(createMyDbPlugin()) .concat(createOtherDbPlugin());Hạ tầng
Phần tiêu đề “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() truy xuất 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 truy cập internet ra ngoài.
Triển khai API vào cùng VPC với cơ sở dữ liệu, cấp quyền rds-db:connect thông qua additional_iam_policy_statements, và mở đường dẫn mạng với một cặp security group rules:
module "my_database" { source = "../../common/terraform/src/app/dbs/my-database" vpc_id = module.vpc.vpc_id database_subnet_ids = module.vpc.private_isolated_subnet_ids lambda_subnet_ids = module.vpc.private_subnet_ids}
module "api" { source = "..." enable_vpc = true vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnet_ids
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}" ] } ]
env = { RUNTIME_CONFIG_APP_ID = module.runtime_config_appconfig.application_id }}
resource "aws_vpc_security_group_ingress_rule" "api_to_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" { 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. RUNTIME_CONFIG_APP_ID nên trỏ đến ứng dụng AppConfig runtime configuration được chia sẻ, không phải output của database module.
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ừ Node.js 20 hoặc các Lambda runtime mới hơn, hãy tải gói Amazon RDS CA 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, xem tài liệu AWS Lambda về yêu cầu SSL/TLS cho kết nối Amazon RDS 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 RDS CA 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ộ”Trình tạo cấu hình target dev của tRPC API để phụ thuộc vào target dev của cơ sở dữ liệu, vì vậy khi chạy:
pnpm nx dev <api-project-name>yarn nx dev <api-project-name>npx nx dev <api-project-name>bunx nx dev <api-project-name>sẽ tự động khởi động cơ sở dữ liệu cục bộ cùng với API của bạn.