tRPC API からリレーショナルデータベースへの接続
connection ジェネレーターは、tRPC API を リレーショナルデータベース プロジェクトに接続し、プロシージャコンテキストで Prisma クライアントを利用できるようにする型安全な tRPC ミドルウェアプラグインを生成します。
このジェネレーターを使用する前に、以下を用意してください:
ts#trpc-apiプロジェクトts#rdbプロジェクト
ジェネレーターの実行
Section titled “ジェネレーターの実行”- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - connection - 必須パラメータを入力
- クリック
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:connectiontRPC API プロジェクトをソースとして選択し、リレーショナルデータベースプロジェクトをターゲットとして選択します。
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| sourceProject 必須 | string | - | ソース プロジェクト |
| targetProject 必須 | string | - | 接続先のターゲット プロジェクト |
| sourceComponent | string | - | 接続元のソース コンポーネント (コンポーネント名、ソース プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをソースとして明示的に選択するには '.' を使用します。 |
| targetComponent | string | - | 接続先のターゲット コンポーネント (コンポーネント名、ターゲット プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをターゲットとして明示的に選択するには '.' を使用します。 |
ジェネレーターの出力
Section titled “ジェネレーターの出力”ジェネレーターは、tRPC API プロジェクト内にミドルウェアファイルを作成します:
Directorypackages/api/src
Directorymiddleware
- <db-name>.ts プロシージャコンテキストで Prisma クライアントを公開する tRPC プラグイン
さらに、tRPC API の serve-local ターゲットを更新して、ローカル実行時にデータベースを自動的に起動するようにします。
ミドルウェアの使用
Section titled “ミドルウェアの使用”プラグインの登録
Section titled “プラグインの登録”生成されたプラグインを tRPC ルーターに追加して、それを使用するすべてのプロシージャがデータベースにアクセスできるようにします:
import { t } from './init.js';import { createMyDbPlugin } from './middleware/my-db.js';
export const authenticatedProcedure = t.procedure .concat(createMyDbPlugin());プロシージャ内でのデータベースへのアクセス
Section titled “プロシージャ内でのデータベースへのアクセス”プラグインは IMyDbContext をプロシージャコンテキストにマージし、myDb をオプショナルプロパティとして利用可能にします:
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 は Prisma クライアント — Awaited<ReturnType<typeof getPrisma>> として型付けされています return await ctx.myDb!.user.findMany(); });MySQL: 各リクエスト後の切断
Section titled “MySQL: 各リクエスト後の切断”ターゲットデータベースが MySQL エンジンを使用している場合、生成されたミドルウェアは opts.next() を try/finally ブロックでラップし、$disconnect() を呼び出します:
return t.procedure.use(async (opts) => { const myDb = await getPrisma(); try { return await opts.next({ ctx: { ...opts.ctx, myDb } }); } finally { await myDb.$disconnect(); }});これは、MySQL アダプターがクエリ後に Node.js イベントループを開いたままにする問題に対処します。これにより、Lambda がストリーミングレスポンスをフラッシュできなくなります。finally で切断することでイベントループが解放され、レスポンスが完了できるようになります。詳細については、MySQL: API Gateway ストリーミングモード を参照してください。
PostgreSQL ではこれは不要です — そのアダプターは allowExitOnIdle: true で設定されたコネクションプールを使用します。
複数のデータベース
Section titled “複数のデータベース”異なるターゲットでジェネレーターを再度実行することで、追加のデータベースを接続できます。各データベースは独自のプラグインとコンテキストインターフェースを取得します:
export const dbProcedure = t.procedure .concat(createPostgresDbPlugin()) .concat(createMySqlDbPlugin());インフラストラクチャ
Section titled “インフラストラクチャ”実行時にAPIがデータベースに接続できるようにするには、API Lambda関数をデータベースと同じVPCにデプロイし、ネットワークおよびIAMアクセスを付与する必要があります。
アプリケーションスタックで、APIをデータベースと同じVPCにデプロイし、allowDefaultPortFromとgrantConnectを呼び出して、ネットワークパスを開き、各Lambdaハンドラーに IAM rds-db:connect権限を付与します:
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);});API Lambda関数は、プライベート分離サブネットではなく、エグレス付きプライベートサブネットにデプロイしてください。実行時に、getPrisma()はAWS AppConfigからデータベース接続の詳細を取得しますが、これはアウトバウンドインターネットアクセスを必要とするパブリックAWSサービスエンドポイントです。
データベースモジュールの出力をAPIモジュールに渡して、データベースに到達し、その実行時設定を読み取れるようにします:
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 = "..." vpc_id = module.vpc.vpc_id private_subnet_ids = module.vpc.private_subnet_ids
appconfig_application_id = module.my_database.appconfig_application_id database_cluster_resource_id = module.my_database.cluster_resource_id database_runtime_user = module.my_database.database_runtime_user database_security_group_id = module.my_database.security_group_id database_port = module.my_database.cluster_port
environment_variables = { RUNTIME_CONFIG_APP_ID = module.my_database.appconfig_application_id }}API Lambda関数は、プライベート分離サブネットではなく、エグレス付きプライベートサブネットにデプロイしてください。API LambdaロールがIAM rds-db:connect権限を持ち、そのセキュリティグループがデータベースポートでデータベースセキュリティグループに到達できることを確認してください。
ローカル開発
Section titled “ローカル開発”ジェネレーターは、tRPC API の serve-local ターゲットがデータベースの serve-local ターゲットに依存するように設定するため、以下を実行すると:
pnpm nx serve-local <api-project-name>yarn nx serve-local <api-project-name>npx nx serve-local <api-project-name>bunx nx serve-local <api-project-name>API と一緒にローカルデータベースが自動的に起動されます.