Skip to content

リレーショナルデータベース

Filter this guide Pick generator option values to hide sections that don't apply.

このジェネレーターは、Amazon Aurora(PostgreSQLまたはMySQL)とPrisma ORMを基盤とする新しいリレーショナルデータベースプロジェクトを作成します。AWS CDKまたはTerraformを使用してデータベースをプロビジョニングおよび管理するために必要なアプリケーションコードとインフラストラクチャを生成し、宣言的なスキーマ定義、自動マイグレーションデプロイ、型安全なORMクライアントを提供します。

リレーショナルデータベースの生成

Section titled “リレーショナルデータベースの生成”

新しいリレーショナルデータベースプロジェクトは、2つの方法で生成できます:

  1. インストール Nx Console VSCode Plugin まだインストールしていない場合
  2. VSCodeでNxコンソールを開く
  3. クリック Generate (UI) "Common Nx Commands"セクションで
  4. 検索 @aws/nx-plugin - ts#rdb
  5. 必須パラメータを入力
    • クリック Generate
    パラメータ デフォルト 説明
    name 必須 string - 生成するデータベースプロジェクトの名前
    directory string packages アプリケーションを保存するディレクトリ
    subDirectory string - プロジェクトが配置されるサブディレクトリ。デフォルトではプロジェクト名になります。
    service 必須 string Aurora プロビジョニングするリレーショナルデータベースサービス
    engine 必須 string PostgreSQL 選択したサービスで使用するデータベースエンジン
    databaseUser string dbadmin データベース管理者のユーザー名。デフォルトは 'dbadmin' です。
    databaseName string - 初期データベース名。デフォルトではプロジェクト名になります。
    ormFramework 必須 string Prisma 生成されるプロジェクトで使用するORMフレームワーク
    iacProvider string Inherit 優先するIaCプロバイダー。デフォルトでは初期選択から継承されます

    ジェネレーターは、<directory>/<name>ディレクトリに以下のプロジェクト構造を作成します:

    • Directoryprisma
      • Directorymodels
        • example.prisma サンプルモデル定義
      • schema.prisma メインPrismaスキーマ(モデルを参照)
    • Directoryscripts
      • docker-pull.ts ローカル開発用のデータベースDockerイメージをプル
      • docker-start.ts ローカルデータベースコンテナを起動
      • wait-for-db.ts ローカルデータベースの準備完了を待機
    • Directorysrc
      • index.ts プロジェクトのエントリーポイント
      • constants.ts ローカル開発接続の詳細とランタイム設定キー
      • prisma.ts Prismaランタイムクライアントラッパー
      • utils.ts ランタイム設定とシークレットヘルパー
      • create-db-user-handler.ts デプロイ時にアプリケーションデータベースユーザーを作成するために使用されるLambdaハンドラー
      • migration-handler.ts デプロイ時にデータベースマイグレーションを実行するために使用されるLambdaハンドラー
    • .gitignore 生成されたPrismaクライアント出力を含むGit ignoreエントリ
    • Dockerfile マイグレーションハンドラーのコンテナイメージ定義
    • project.json プロジェクト設定とビルドターゲット
    • prisma.config.ts Prisma CLIの設定

    このジェネレータは選択した iacProvider に基づいてInfrastructure as Codeを生成するため、packages/common に関連するCDKコンストラクトまたはTerraformモジュールを含むプロジェクトを作成します。

    共通のInfrastructure as Codeプロジェクトは以下の構造を持ちます:

    • Directorypackages/common/constructs
      • Directorysrc
        • Directoryapp/ プロジェクト/ジェネレータ固有のインフラストラクチャ用コンストラクト
        • Directorycore/ app 内のコンストラクトで再利用される汎用コンストラクト
        • index.ts app からコンストラクトをエクスポートするエントリーポイント
      • project.json プロジェクトのビルドターゲットと設定
    • Directorypackages/common/constructs/src
      • Directoryapp
        • Directorydbs
          • <name>.ts データベース固有のインフラストラクチャ
      • Directorycore
        • Directoryrdb
          • aurora.ts 汎用Auroraデータベースコンストラクト

    生成されたプロジェクトは、データベーススキーマの定義と型安全なクライアントの生成にPrisma ORMを使用します。ワークフローはモデルファーストです: データベースプロジェクトのprisma/models/ディレクトリ配下にPrismaモデルファイルを追加または更新し、それらのモデル変更からマイグレーションを生成します。

    Userモデルの例:

    packages/postgres/prisma/models/user.prisma
    model User {
    id Int @id @default(autoincrement())
    firstName String
    lastName String
    }

    詳細については、公式のPrismaデータモデリングガイドを参照してください。

    データベースクライアントの生成

    Section titled “データベースクライアントの生成”

    ジェネレーターは、プロジェクトをビルドするたびに型安全なTypeScript Prismaクライアントを作成するようにgenerateターゲットを自動的に構成します。クライアントはgenerated/prismaに書き込まれます(.gitignoreに追加されます)。

    また、いつでも手動でクライアントを生成できます:

    Terminal window
    pnpm nx generate <your-db-project-name>

    ワークスペースルートからprismaターゲットを使用してPrisma CLIコマンドを実行します:

    Terminal window
    pnpm nx run <project>:prisma generate

    src/prisma.tsのランタイムラッパーは以下をエクスポートします:

    • DB_PACKAGE_NAME - AWS AppConfigのdatabaseランタイム設定名前空間で使用されるキー
    • getPrisma() - AWS AppConfigからデータベース接続設定を読み込み、IAM認証を使用してPrismaクライアントを作成します

    クライアントは自動的に以下を行います:

    • RUNTIME_CONFIG_APP_ID環境変数を使用してAWS AppConfigからデータベース設定を取得
    • IAM認証用にAWS RDS Signerを介して一時的な認証トークンを生成
    • 証明書検証を使用してSSL/TLS接続を管理
    • 永続的なデータベース接続プールを通じて接続プーリングを処理

    prisma/models/配下にモデルを追加または更新した後、migrate devを使用してマイグレーションファイルを生成し、同時にローカルデータベースに適用します。

    生成されたprismaターゲットは、実行前にDocker経由でローカルデータベースを自動的に起動します:

    Terminal window
    pnpm nx run <project>:prisma migrate dev

    ローカルデータベースに適用せずにマイグレーションファイルのみを生成したい場合は、--create-onlyを追加します:

    Terminal window
    pnpm nx run <project>:prisma migrate dev --create-only

    これにより、スキーマが変更されるたびにprisma/migrationsに新しいマイグレーションフォルダが生成されます:

    • Directoryprisma
      • Directorymigrations
        • Directory20260405013911_initial_migrations
          • migration.sql
        • migration_lock.toml
      • schema.prisma

    AWSスタックをデプロイすると、生成されたインフラストラクチャが生成されたマイグレーションをデプロイされたデータベースに自動的に適用します。

    既存のマイグレーションの適用

    Section titled “既存のマイグレーションの適用”

    他の開発者が作成したマイグレーションファイルをプルした場合、migrate deployを使用してそれらの既存のマイグレーションをローカルデータベースに適用します。

    Terminal window
    pnpm nx run <project>:prisma migrate deploy

    このローカル開発フローでは、migrate deployはマイグレーションファイルをローカルデータベースに適用します。データベースをAWSにデプロイするわけではありません。

    生成されたprismaターゲットはPrisma CLIを公開しているため、ローカルデータベースに対してPrismaがサポートする任意のコマンドを実行できます。利用可能なコマンドについては、Prisma CLIリファレンスを参照してください。

    Terminal window
    pnpm nx run <project>:prisma <prisma-command>

    Prisma Studioは、ローカルデータベース用のビジュアルエディタです。テーブルの閲覧、レコードの検査と編集、データのフィルタリング、リレーションのフォロー、組み込みSQLコンソールを介した生SQLの実行に使用できます。マイグレーションの検証や開発中のテストデータのシードに便利です。次のコマンドで起動します:

    Terminal window
    pnpm nx run <project>:prisma studio

    このセクションでは、tRPC APIからデータベースに接続する方法を説明していますが、他のTypeScriptプロジェクトでの使用の参考にもなります。

    ハンドラーでのPrismaクライアントの使用

    Section titled “ハンドラーでのPrismaクライアントの使用”

    データベースパッケージからgetPrismaをインポートし、ハンドラー内で呼び出して型安全なPrismaクライアントを取得します:

    packages/api/src/procedures/list-users.ts
    import { getPrisma } from ':my-scope/db';
    import { publicProcedure } from '../init.js';
    import { ListUsersOutputSchema } from '../schema/index.js';
    export const listUsers = publicProcedure
    .output(ListUsersOutputSchema)
    .query(async () => {
    const prisma = await getPrisma();
    return prisma.user.findMany({ orderBy: { id: 'asc' } });
    });

    getPrisma()は遅延初期化されたキャッシュされたクライアントを返します。同じLambda実行コンテキスト内での後続の呼び出しは、新しい接続を開くのではなく、既存の接続プールを再利用します。

    Prismaクライアントは、prisma/models/スキーマから派生した完全に型付けされたモデルを公開し、データベースからAPIレスポンスまでのエンドツーエンドの型安全性を提供します。

    ミドルウェアを介したPrismaクライアントの注入

    Section titled “ミドルウェアを介したPrismaクライアントの注入”

    すべてのプロシージャでgetPrisma()を呼び出すのではなく、ミドルウェアで一度解決してtRPCコンテキストにアタッチすることで、すべての下流プロシージャが直接アクセスできるようにすることもできます。

    まず、生成されたミドルウェアと同じパターンに従って、src/middleware/db.tsでプラグインを定義します:

    packages/api/src/middleware/db.ts
    import { getPrisma } from ':my-scope/db';
    import { initTRPC } from '@trpc/server';
    export interface IDbContext {
    db: Awaited<ReturnType<typeof getPrisma>>;
    }
    export const createDbPlugin = () => {
    const t = initTRPC.context<IDbContext>().create();
    return t.procedure.use(async (opts) => {
    const db = await getPrisma();
    return opts.next({
    ctx: {
    ...opts.ctx,
    db,
    },
    });
    });
    };

    次に、tRPCの初期化でベースプロシージャに連結します:

    packages/api/src/init.ts
    import { createDbPlugin } from './middleware/db.js';
    export const dbProcedure = publicProcedure.concat(createDbPlugin());

    dbProcedure上に構築されたプロシージャは、getPrisma()をインポートまたは呼び出す必要なく、コンテキストを通じてdbを受け取ります:

    packages/api/src/procedures/list-users.ts
    import { dbProcedure } from '../init.js';
    import { ListUsersOutputSchema } from '../schema/index.js';
    export const listUsers = dbProcedure
    .output(ListUsersOutputSchema)
    .query(async ({ ctx: { db } }) => {
    return db.user.findMany({ orderBy: { id: 'asc' } });
    });

    リレーショナルデータベースジェネレーターは、選択したiacProviderに基づいてCDKまたはTerraformのインフラストラクチャを作成します。

    CDKコンストラクトはcommon/constructsに作成されます。使用例:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    export class ApplicationStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    ...
    const db = new MyDatabase(this, 'Db', {
    vpc,
    vpcSubnets: {
    subnetType: SubnetType.PRIVATE_ISOLATED,
    }
    });
    }
    }

    これにより、RDS Proxy、管理者認証情報、アプリケーションデータベースユーザー、ランタイム設定の登録、マイグレーションハンドラーを備えたAuroraクラスターがプロビジョニングされます。

    生成されたインフラストラクチャは、2つのデータベースユーザーを作成します:

    • 管理者ユーザー - クラスターのプロビジョニング時に作成され、認証情報はAWS Secrets Managerに保存されます
    • アプリケーションユーザー - Lambda カスタムリソースを介して作成され、IAM認証が有効化され、アプリケーションデータベースに対する完全な権限を持ちます

    アプリケーションユーザーは、ランダムな名前とIAM認証で自動的に作成されます。getPrisma()は、短期間有効なRDSトークンを使用してこのユーザーとして認証するように既に構成されているため、アプリケーションコードがデータベースパスワードを処理することはありません。

    VPCには、パブリックサブネット、エグレスを持つプライベートサブネット、プライベート分離サブネットを含める必要があります。データベースはプライベート分離サブネットで実行でき、API Lambda関数はエグレスを持つプライベートサブネットで実行して、AppConfigなどのAWSサービスに到達できるようにする必要があります。

    packages/infra/src/stacks/application-stack.ts
    const vpc = new Vpc(this, 'Vpc', {
    subnetConfiguration: [
    {
    name: 'public',
    subnetType: SubnetType.PUBLIC,
    },
    {
    name: 'private_with_egress',
    subnetType: SubnetType.PRIVATE_WITH_EGRESS,
    },
    {
    name: 'private_isolated',
    subnetType: SubnetType.PRIVATE_ISOLATED,
    },
    ],
    });

    アプリケーションスタックで、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 Api(this, 'Api', {
    integrations: Api.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サービスエンドポイントです。プライベート分離サブネット内のLambda関数には、アウトバウンドインターネットアクセスがなく、AppConfigに到達できません。エグレスを持つプライベートサブネットは、パブリックサブネットに配置されたNAT Gatewayを介してアウトバウンドトラフィックをルーティングします。

    生成されたインフラストラクチャには、デフォルトでRDS Proxyが含まれており、アプリケーションとAuroraクラスターの間に配置されます。RDS Proxyは、いくつかの利点を提供します:

    • 接続プーリング - アプリケーションインスタンス間で共有できるデータベース接続のプールを維持し、新しい接続を確立するオーバーヘッドを削減します
    • 接続の回復力 - Auroraインスタンスの交換やメンテナンス中のフェイルオーバーと再接続を自動的に処理します
    • IAM認証 - IAMベースのデータベース認証をサポートし、アプリケーションコードでデータベース認証情報を管理する必要がなくなります
    • セキュリティの向上 - すべての接続にTLS暗号化を強制します

    RDS Proxyは次のように無効にできます:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    enableRdsProxy: false,
    });

    RDS Proxyが無効になっている場合、アプリケーションはAuroraクラスターエンドポイントに直接接続します。

    Node.js 20以降のLambdaランタイムからAuroraクラスターに直接接続する場合は、Amazon RDS CAバンドルを読み込むようにLambda関数を構成します:

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

    詳細については、AWS LambdaのAmazon RDS接続のSSL/TLS要件およびAmazon RDS ProxyのTLSドキュメントを参照してください。RDS Proxyを使用する場合、Lambda関数でRDS CAバンドルを構成する必要はありません。

    生成されたインフラストラクチャは、ワークロードの要件に合わせてカスタマイズできます。以下の例は、利用可能な一般的なカスタマイズオプションのいくつかを示しています。

    Auroraクラスターのwriterおよびreaderインスタンスを構成します。

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    writer: ClusterInstance.serverlessV2('writer'),
    readers: [ClusterInstance.serverlessV2('reader')],
    });

    ワークロードに合わせてAurora Serverless v2のスケーリング制限を制御します。

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    serverlessV2MinCapacity: 0.5,
    serverlessV2MaxCapacity: 8,
    });

    特定のAuroraエンジンバージョンを固定します。

    デフォルトでは、生成されたローカルDockerデータベースイメージは、デフォルトのAuroraエンジンバージョンと一致します。Auroraエンジンバージョンを変更する場合は、最大限の互換性を得るために、一致するローカルDockerデータベースバージョンも使用することをお勧めします。対応するコミュニティデータベースバージョンを特定するには、Aurora PostgreSQLバージョンおよびAurora MySQLバージョン3の更新のAWSリリースノートを参照してください。

    ローカルデータベースイメージは、生成されたデータベースプロジェクトのproject.jsonserve-localターゲットで構成されています。エンジンバージョンを変更する際は、scripts/docker-start.tsに渡されるイメージ引数を更新してください。

    engine = PostgreSQL
    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    engineVersion: AuroraPostgresEngineVersion.VER_17_7,
    });
    engine = MySQL
    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    engineVersion: AuroraMysqlEngineVersion.VER_3_12_0,
    });

    削除保護は、デフォルトで有効になっており(CDKではdeletionProtection: true、Terraformではdeletion_protection = true)、Auroraクラスターを誤って削除から保護します。

    データベースの削除が予想される環境(短期間の開発環境やプレビュースタックなど)では、削除保護を無効にできます。

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    deletionProtection: false,
    });

    CDKコンストラクトは、デフォルトでAuroraクラスターを保持します(removalPolicy: RemovalPolicy.RETAIN)。CDKスタックの削除時にクラスターをスナップショットまたは破棄したい場合は、これを変更してください。

    RemovalPolicy.DESTROYを使用する場合、クラスターを削除する前に削除保護も無効にする必要があります。

    packages/infra/src/stacks/application-stack.ts
    import { RemovalPolicy } from 'aws-cdk-lib';
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    removalPolicy: RemovalPolicy.SNAPSHOT,
    });

    スタックと共にデータベースを削除する必要がある一時的な環境の場合:

    packages/infra/src/stacks/application-stack.ts
    import { RemovalPolicy } from 'aws-cdk-lib';
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    deletionProtection: false,
    removalPolicy: RemovalPolicy.DESTROY,
    });

    Auroraクラスターとその認証情報シークレットの暗号化に使用されるKMSキーは、デフォルトで自動キーローテーションが有効になっています。セキュリティポリシーが外部でローテーションを管理している場合は、無効にしてください。

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    enableKeyRotation: false,
    });
    engine = MySQL

    MySQL: API Gatewayストリーミングモード

    Section titled “MySQL: API Gatewayストリーミングモード”

    Aurora MySQLをAPI Gatewayストリーミングレスポンス(例:tRPCのhttpBatchStreamLink)と併用する場合、Prisma MySQLクライアントはクエリ完了後もNode.jsイベントループを保持し、Lambdaがストリームをフラッシュしてリクエストを終了できなくなります。

    この問題を回避するには、各クエリ後にfinallyブロックでクライアントを明示的に切断し、イベントループが終了してストリーミングレスポンスが完了できるようにします。

    オプション1: プロシージャごと

    export const listExampleTable = publicProcedure
    .output(z.array(ExampleTableSchema))
    .query(async () => {
    const prisma = await getPrisma();
    try {
    return await prisma.exampleTable.findMany();
    } finally {
    await prisma.$disconnect();
    }
    });

    オプション2: tRPCミドルウェア

    ミドルウェアパターンを使用している場合は、ミドルウェアに$disconnect()呼び出しを追加して、それに基づいて構築されたすべてのプロシージャが自動的にカバーされるようにします:

    packages/api/src/middleware/db.ts
    import { getPrisma } from ':my-scope/db';
    import { initTRPC } from '@trpc/server';
    export interface IDbContext {
    db: Awaited<ReturnType<typeof getPrisma>>;
    }
    export const createDbPlugin = () => {
    const t = initTRPC.context<IDbContext>().create();
    return t.procedure.use(async (opts) => {
    const db = await getPrisma();
    try {
    return await opts.next({
    ctx: {
    ...opts.ctx,
    db,
    },
    });
    } finally {
    await db.$disconnect();
    }
    });
    };

    RDS IAM認証トークンは15分後に期限切れになります。MySQL Prismaクライアントは、getPrisma()が呼び出された時点でIAMトークンを静的な値としてキャプチャします。既存の開いている接続は影響を受けませんが、トークンの有効期限が切れた後に新しい接続を確立する必要がある場合、認証は失敗します。PostgreSQLアダプターは、プールが新しい接続を開くたびにトークンを動的に更新することでこれを回避しますが、MySQLアダプターには同等のメカニズムがありません。

    バッチジョブやデータ移行などの長時間実行タスクの場合は、操作全体に対して一度ではなく、各作業単位の開始時にgetPrisma()を呼び出してください。getPrisma()はMySQLに対して常に新しいクライアントを作成し、新しいIAMトークンを取得するため、各接続が有効なトークンで認証されることが保証されます。