Smithy API からリレーショナルデータベースへの接続
connection ジェネレーターは、Smithy API を リレーショナルデータベース プロジェクトに接続し、Prisma クライアントをサービスコンテキストに注入することで、すべての操作実装がデータベースにアクセスできるようにします。
このジェネレーターを使用する前に、以下を用意してください:
ts#smithy-apiプロジェクト(TypeScript バックエンド)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:connectionソースとして Smithy API バックエンドプロジェクトを選択し、ターゲットとしてリレーショナルデータベースプロジェクトを選択します。
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| sourceProject 必須 | string | - | ソース プロジェクト |
| targetProject 必須 | string | - | 接続先のターゲット プロジェクト |
| sourceComponent | string | - | 接続元のソース コンポーネント (コンポーネント名、ソース プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをソースとして明示的に選択するには '.' を使用します。 |
| targetComponent | string | - | 接続先のターゲット コンポーネント (コンポーネント名、ターゲット プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをターゲットとして明示的に選択するには '.' を使用します。 |
ジェネレーターの出力
Section titled “ジェネレーターの出力”ジェネレーターは、Smithy API バックエンドの既存の3つのファイルを変更します:
Directorypackages/api/src
- context.ts
ServiceContextにdbプロパティが追加されます - handler.ts
lambdaHandler内で Prisma クライアントが作成され、serviceHandler.handleに渡されます - local-server.ts リクエストハンドラー内で Prisma クライアントが作成され、
serviceHandler.handleに渡されます
- context.ts
さらに、API の serve-local ターゲットを更新して、データベースを自動的に起動するようにします。
動作の仕組み
Section titled “動作の仕組み”ServiceContext
Section titled “ServiceContext”ジェネレーターは、context.ts の ServiceContext に型付きの db プロパティを追加します:
import { getPrisma as getMyDb } from ':my-scope/my-db';
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>;}Lambda ハンドラー
Section titled “Lambda ハンドラー”Prisma クライアントは lambdaHandler 内でインスタンス化され、サービスコンテキストを通じて渡されます:
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);};操作内でのデータベースの使用
Section titled “操作内でのデータベースの使用”操作実装内で、コンテキストから db にアクセスします:
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 };};インフラストラクチャ
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 “ローカル開発”ジェネレーターは、local-server.ts のリクエストハンドラー内でも同じ Prisma クライアントの注入を適用します:
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);});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 とローカルデータベースの両方が起動します。SERVE_LOCAL=true 環境変数が自動的に設定されるため、Prisma クライアントは Aurora ではなくローカルの Docker データベースに接続します。