Skip to content

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

connection ジェネレーターは、Smithy APIリレーショナルデータベース プロジェクトに接続し、Prisma クライアントをサービスコンテキストに注入することで、すべての操作実装がデータベースにアクセスできるようにします。

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

  1. Smithy TypeScript API プロジェクト(ts#api--framework=smithy で生成)
  2. ts#rdb プロジェクト
Terminal window
pnpm nx g @aws/nx-plugin:connection
変更されるファイルを確認するためにドライランを実行することもできます
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run

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

パラメータデフォルト説明
sourceProject 必須string-ソース プロジェクト
targetProject 必須string-接続先のターゲット プロジェクト
sourceComponent string-接続元のソース コンポーネント (コンポーネント名、ソース プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをソースとして明示的に選択するには '.' を使用します。
targetComponent string-接続先のターゲット コンポーネント (コンポーネント名、ターゲット プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをターゲットとして明示的に選択するには '.' を使用します。
preferInstallDependencies booleantrueジェネレーター実行後に依存関係のインストールを優先するかどうか。複数のジェネレーターをバッチ処理する際にインストールを延期する場合はfalseに設定します(後続のジェネレーターがNxプロジェクトグラフを計算できるよう、必要に応じてインストールは実行されます)。最後に一度だけインストールします。

ジェネレーターは、Smithy API バックエンド内の既存の3つのファイルを変更します:

  • Directorypackages/api/src
    • context.ts db プロパティが ServiceContext に追加されます
    • handler.ts Prisma クライアントが lambdaHandler 内で作成され、serviceHandler.handle に渡されます
    • local-server.ts Prisma クライアントがリクエストハンドラー内で作成され、serviceHandler.handle に渡されます

さらに、API の dev ターゲットを更新して、データベースを自動的に起動するようにします。

ジェネレーターは、context.tsServiceContext に型付きの db プロパティを追加します:

packages/api/src/context.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
myDb: Awaited<ReturnType<typeof getMyDb>>;
}

Prisma クライアントは lambdaHandler 内でインスタンス化され、サービスコンテキストを通じて渡されます:

packages/api/src/handler.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
export const lambdaHandler = async (event: APIGatewayProxyEvent) => {
const httpRequest = convertEvent(event);
const myDb = await getMyDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
});
return convertVersion1Response(httpResponse);
};

操作実装でコンテキストから db にアクセスします:

packages/api/src/operations/list-users.ts
import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';
import { ServiceContext } from '../context.js';
export const listUsers = async (
input: ListUsersOperationInput,
ctx: ServiceContext,
): Promise<ListUsersOperationOutput> => {
const users = await ctx.myDb.user.findMany();
return { users };
};

異なるターゲットでジェネレーターを再度実行すると、最初のデータベースと並んで2番目のデータベースが追加されます。両方のクライアントが ServiceContext に追加され、handler.ts でインスタンス化されます:

packages/api/src/context.ts
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
myDb: Awaited<ReturnType<typeof getMyDb>>;
otherDb: Awaited<ReturnType<typeof getOtherDb>>;
}
packages/api/src/handler.ts
const myDb = await getMyDb();
const otherDb = await getOtherDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
otherDb,
});

実行時に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サービスエンドポイントです。

RDS Proxy を使用せずに接続する場合の SSL 要件

Section titled “RDS Proxy を使用せずに接続する場合の SSL 要件”

Node.js 20以降のLambdaランタイムからAuroraクラスターに直接接続する場合は、NODE_EXTRA_CA_CERTSを設定してAmazon RDS CAバンドルを読み込んでください:

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バンドルを設定する必要はありません。

ジェネレーターは、local-server.ts のリクエストハンドラー内でも同じ Prisma クライアントの注入を適用します:

packages/api/src/local-server.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
const server = createServer(async function (req, res) {
const httpRequest = convertRequest(req);
const myDb = await getMyDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
});
return writeResponse(httpResponse, res);
});
Terminal window
pnpm nx dev <api-project-name>

これにより、API とローカルデータベースの両方が起動します。LOCAL_DEV=true 環境変数が自動的に設定されるため、Prisma クライアントは Aurora の代わりにローカルの Docker データベースに接続します。