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

API tRPC đến Cơ sở dữ liệu Quan hệ

Trình tạo connection kết nối một API tRPC với dự án Cơ sở dữ liệu Quan hệ, tạo ra một plugin middleware tRPC an toàn về kiểu giúp cung cấp Prisma client trong ngữ cảnh thủ tục của bạn.

Trước khi sử dụng trình tạo này, hãy đảm bảo bạn có:

  1. Một dự án API tRPC (được tạo bằng ts#api)
  2. Một dự án ts#rdb
Terminal window
pnpm nx g @aws/nx-plugin:connection
Bạn cũng có thể thực hiện chạy thử để xem những tệp nào sẽ bị thay đổi
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run

Chọn dự án API tRPC 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.

Tham sốKiểuMặc địnhMô tả
sourceProject Bắt buộcstring-Dự án nguồn
targetProject Bắt buộcstring-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 booleantrueCó 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.

Trình tạo tạo một tệp middleware trong dự án API tRPC của bạn:

  • Thư mụcpackages/api/src
    • Thư mụcmiddleware
      • <db-name>.ts tRPC plugin exposing the Prisma client in procedure context

Ngoài ra, nó cập nhật target dev của API tRPC để tự động khởi động cơ sở dữ liệu khi chạy cục bộ.

Thêm plugin được tạo vào router tRPC của bạn để tất cả các thủ tục sử dụng nó có quyền truy cập vào cơ sở dữ liệu:

packages/api/src/router.ts
import { t } from './init.js';
import { createMyDbPlugin } from './middleware/my-db.js';
export const authenticatedProcedure = t.procedure
.concat(createMyDbPlugin());

Plugin hợp nhất IMyDbContext vào ngữ cảnh thủ tục của bạn, làm cho myDb có sẵn dưới dạng thuộc tính tùy chọn:

packages/api/src/procedures/users.ts
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 is the Prisma client — typed as Awaited<ReturnType<typeof getPrisma>>
return await ctx.myDb!.user.findMany();
});

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():

packages/api/src/middleware/my-db.ts
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.

Bạn có thể kết nối thêm 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:

packages/api/src/router.ts
export const dbProcedure = t.procedure
.concat(createMyDbPlugin())
.concat(createOtherDbPlugin());

Để 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 allowDefaultPortFromgrantConnect để mở đường dẫn mạng và cấp quyền IAM rds-db:connect cho mỗi Lambda handler:

packages/infra/src/stacks/application-stack.ts
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.

Yêu cầu SSL Khi Kết nối Không qua RDS Proxy

Phần tiêu đề “Yêu cầu SSL Khi Kết nối Không qua 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:

packages/infra/src/stacks/application-stack.ts
const api = new Api(this, 'Api', {
integrations: Api.defaultIntegrations(this)
.withDefaultOptions({
environment: {
NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem',
},
})
.build(),
});

Để 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.

Trình tạo cấu hình target dev của API tRPC để phụ thuộc vào target dev của cơ sở dữ liệu, vì vậy khi chạy:

Terminal window
pnpm 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.