Skip to content

tRPC API からリレーショナルデータベースへの接続

connection ジェネレーターは、tRPC APIリレーショナルデータベース プロジェクトに接続し、プロシージャコンテキストで Prisma クライアントを利用できるようにする型安全な tRPC ミドルウェアプラグインを生成します。

このジェネレーターを使用する前に、以下を用意してください:

  1. ts#trpc-api プロジェクト
  2. ts#rdb プロジェクト
  1. インストール Nx Console VSCode Plugin まだインストールしていない場合
  2. VSCodeでNxコンソールを開く
  3. クリック Generate (UI) "Common Nx Commands"セクションで
  4. 検索 @aws/nx-plugin - connection
  5. 必須パラメータを入力
    • クリック Generate

    tRPC API プロジェクトをソースとして選択し、リレーショナルデータベースプロジェクトをターゲットとして選択します。

    パラメータ デフォルト 説明
    sourceProject 必須 string - ソース プロジェクト
    targetProject 必須 string - 接続先のターゲット プロジェクト
    sourceComponent string - 接続元のソース コンポーネント (コンポーネント名、ソース プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをソースとして明示的に選択するには '.' を使用します。
    targetComponent string - 接続先のターゲット コンポーネント (コンポーネント名、ターゲット プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをターゲットとして明示的に選択するには '.' を使用します。

    ジェネレーターは、tRPC API プロジェクト内にミドルウェアファイルを作成します:

    • Directorypackages/api/src
      • Directorymiddleware
        • <db-name>.ts プロシージャコンテキストで Prisma クライアントを公開する tRPC プラグイン

    さらに、tRPC API の serve-local ターゲットを更新して、ローカル実行時にデータベースを自動的に起動するようにします。

    生成されたプラグインを tRPC ルーターに追加して、それを使用するすべてのプロシージャがデータベースにアクセスできるようにします:

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

    プロシージャ内でのデータベースへのアクセス

    Section titled “プロシージャ内でのデータベースへのアクセス”

    プラグインは IMyDbContext をプロシージャコンテキストにマージし、myDb をオプショナルプロパティとして利用可能にします:

    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 は Prisma クライアント — Awaited<ReturnType<typeof getPrisma>> として型付けされています
    return await ctx.myDb!.user.findMany();
    });

    ターゲットデータベースが MySQL エンジンを使用している場合、生成されたミドルウェアは opts.next()try/finally ブロックでラップし、$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();
    }
    });

    これは、MySQL アダプターがクエリ後に Node.js イベントループを開いたままにする問題に対処します。これにより、Lambda がストリーミングレスポンスをフラッシュできなくなります。finally で切断することでイベントループが解放され、レスポンスが完了できるようになります。詳細については、MySQL: API Gateway ストリーミングモード を参照してください。

    PostgreSQL ではこれは不要です — そのアダプターは allowExitOnIdle: true で設定されたコネクションプールを使用します。

    異なるターゲットでジェネレーターを再度実行することで、追加のデータベースを接続できます。各データベースは独自のプラグインとコンテキストインターフェースを取得します:

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

    実行時にAPIがデータベースに接続できるようにするには、API Lambda関数をデータベースと同じVPCにデプロイし、ネットワークおよびIAMアクセスを付与する必要があります。

    アプリケーションスタックで、APIをデータベースと同じVPCにデプロイし、allowDefaultPortFromgrantConnectを呼び出して、ネットワークパスを開き、各Lambdaハンドラーに IAM rds-db:connect権限を付与します:

    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);
    });

    API Lambda関数は、プライベート分離サブネットではなく、エグレス付きプライベートサブネットにデプロイしてください。実行時に、getPrisma()はAWS AppConfigからデータベース接続の詳細を取得しますが、これはアウトバウンドインターネットアクセスを必要とするパブリックAWSサービスエンドポイントです。

    ジェネレーターは、tRPC API の serve-local ターゲットがデータベースの serve-local ターゲットに依存するように設定するため、以下を実行すると:

    Terminal window
    pnpm nx serve-local <api-project-name>

    API と一緒にローカルデータベースが自動的に起動されます.