@aws/nx-plugin-mcp 0.119.0 → 0.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/bin/aws-nx-mcp.js +23 -35
  2. package/docs/guides/astro-docs.mdx +142 -0
  3. package/docs/guides/connection/py-agent-a2a.mdx +105 -0
  4. package/docs/guides/connection/py-agent-mcp.mdx +147 -0
  5. package/docs/guides/connection/react-agui.mdx +240 -0
  6. package/docs/guides/connection/react-fastapi.mdx +1476 -0
  7. package/docs/guides/connection/react-py-agent.mdx +198 -0
  8. package/docs/guides/connection/react-smithy.mdx +1355 -0
  9. package/docs/guides/connection/react-trpc.mdx +447 -0
  10. package/docs/guides/connection/react-ts-agent.mdx +198 -0
  11. package/docs/guides/connection/smithy-rdb.mdx +161 -0
  12. package/docs/guides/connection/trpc-rdb.mdx +127 -0
  13. package/docs/guides/connection/ts-agent-a2a.mdx +106 -0
  14. package/docs/guides/connection/ts-agent-mcp.mdx +144 -0
  15. package/docs/guides/connection/ts-agent-rdb.mdx +141 -0
  16. package/docs/guides/connection/ts-mcp-server-rdb.mdx +135 -0
  17. package/docs/guides/connection.mdx +126 -0
  18. package/docs/guides/docker-bundling.mdx +403 -0
  19. package/docs/guides/fastapi.mdx +509 -0
  20. package/docs/guides/license.mdx +293 -0
  21. package/docs/guides/nx-generator.mdx +588 -0
  22. package/docs/guides/py-agent.mdx +483 -0
  23. package/docs/guides/py-mcp-server.mdx +161 -0
  24. package/docs/guides/python-lambda-function.mdx +207 -0
  25. package/docs/guides/python-project.mdx +228 -0
  26. package/docs/guides/react-website-auth.mdx +216 -0
  27. package/docs/guides/react-website.mdx +421 -0
  28. package/docs/guides/runtime-config.mdx +312 -0
  29. package/docs/guides/terraform-project.mdx +317 -0
  30. package/docs/guides/trpc.mdx +816 -0
  31. package/docs/guides/ts-agent.mdx +436 -0
  32. package/docs/guides/ts-lambda-function.mdx +218 -0
  33. package/docs/guides/ts-mcp-server.mdx +169 -0
  34. package/docs/guides/ts-nx-plugin.mdx +159 -0
  35. package/docs/guides/ts-rdb.mdx +759 -0
  36. package/docs/guides/ts-smithy-api.mdx +661 -0
  37. package/docs/guides/typescript-infrastructure.mdx +408 -0
  38. package/docs/guides/typescript-project.mdx +312 -0
  39. package/docs/guides/workspace.mdx +181 -0
  40. package/docs/snippets/agent/architecture.mdx +72 -0
  41. package/docs/snippets/agent/bedrock-deployment.mdx +172 -0
  42. package/docs/snippets/agent/runtime-arn.mdx +64 -0
  43. package/docs/snippets/api/api-architecture.mdx +93 -0
  44. package/docs/snippets/api/api-choice-note.mdx +6 -0
  45. package/docs/snippets/api/cors-configuration-cdk-note.mdx +25 -0
  46. package/docs/snippets/api/cors-configuration-terraform-note.mdx +28 -0
  47. package/docs/snippets/api/shared-constructs.mdx +38 -0
  48. package/docs/snippets/api/type-safe-api-integrations.mdx +643 -0
  49. package/docs/snippets/api/waf-configuration.mdx +37 -0
  50. package/docs/snippets/connection/a2a-infrastructure.mdx +63 -0
  51. package/docs/snippets/connection/lambda-rdb-ssl-requirements.mdx +40 -0
  52. package/docs/snippets/connection/mcp-server-rdb-ssl-requirements.mdx +35 -0
  53. package/docs/snippets/connection/rdb-api-infrastructure.mdx +72 -0
  54. package/docs/snippets/connection/react-agent-infrastructure.mdx +61 -0
  55. package/docs/snippets/connection/strands-agent-rdb-ssl-requirements.mdx +35 -0
  56. package/docs/snippets/lambda-function/architecture.mdx +36 -0
  57. package/docs/snippets/lambda-function/deploying-your-function.mdx +118 -0
  58. package/docs/snippets/mcp/architecture.mdx +58 -0
  59. package/docs/snippets/mcp/assistant-docs.mdx +10 -0
  60. package/docs/snippets/mcp/bedrock-deployment.mdx +167 -0
  61. package/docs/snippets/mcp/config.mdx +13 -0
  62. package/docs/snippets/mcp/configuration-py.mdx +42 -0
  63. package/docs/snippets/mcp/configuration-ts.mdx +53 -0
  64. package/docs/snippets/mcp/observability.mdx +8 -0
  65. package/docs/snippets/mcp/shared-constructs.mdx +32 -0
  66. package/docs/snippets/pdk-migration/example/01-migrate-api.mdx +602 -0
  67. package/docs/snippets/pdk-migration/example/02-migrate-website.mdx +915 -0
  68. package/docs/snippets/pdk-migration/example/03-migrate-infra.mdx +161 -0
  69. package/docs/snippets/pdk-migration/example/04-deploy.mdx +229 -0
  70. package/docs/snippets/pdk-migration/faq/aws-arch.mdx +17 -0
  71. package/docs/snippets/pdk-migration/faq/cdk-graph.mdx +29 -0
  72. package/docs/snippets/pdk-migration/faq/infrastructure-python-java.mdx +19 -0
  73. package/docs/snippets/pdk-migration/faq/pdk-nag.mdx +15 -0
  74. package/docs/snippets/pdk-migration/faq/pipeline.mdx +15 -0
  75. package/docs/snippets/pdk-migration/faq/type-safe-api.mdx +310 -0
  76. package/docs/snippets/pdk-migration/faq/use-of-projen.mdx +15 -0
  77. package/docs/snippets/prerequisites.mdx +20 -0
  78. package/docs/snippets/required-prerequisites.mdx +12 -0
  79. package/docs/snippets/shared-constructs.mdx +40 -0
  80. package/docs/snippets/tools/acurl.mdx +73 -0
  81. package/docs/snippets/ts-bundle.mdx +14 -0
  82. package/package.json +1 -1
  83. package/src/py/agent/schema.json +1 -1
  84. package/src/py/fast-api/schema.json +2 -2
  85. package/src/py/mcp-server/schema.json +1 -1
  86. package/src/smithy/ts/api/schema.json +2 -2
  87. package/src/trpc/backend/schema.json +2 -2
  88. package/src/ts/agent/schema.json +1 -1
  89. package/src/ts/mcp-server/schema.json +1 -1
@@ -0,0 +1,759 @@
1
+ ---
2
+ title: Relational Database
3
+ description: Create a relational database project
4
+ generator: ts#rdb
5
+ ---
6
+
7
+ import { FileTree } from '@astrojs/starlight/components';
8
+ import Link from '@components/link.astro';
9
+ import RunGenerator from '@components/run-generator.astro';
10
+ import GeneratorParameters from '@components/generator-parameters.astro';
11
+ import Infrastructure from '@components/infrastructure.astro';
12
+ import NxCommands from '@components/nx-commands.astro';
13
+ import PackageManagerExecCommand from '@components/package-manager-exec-command.astro';
14
+ import Snippet from '@components/snippet.astro';
15
+ import OptionFilter from '@components/option-filter.astro';
16
+
17
+ This generator creates a new relational database project backed by [Amazon Aurora](https://aws.amazon.com/rds/aurora/) (PostgreSQL or MySQL) and [Prisma ORM](https://www.prisma.io/docs/orm). It generates the application code and infrastructure needed to provision and manage a database using AWS CDK or Terraform, with declarative schema definition, automatic migration deployment, and a type-safe ORM client.
18
+
19
+ ## Usage
20
+
21
+ ### Generate a Relational Database
22
+
23
+ You can generate a new relational database project in two ways:
24
+
25
+ <RunGenerator generator="ts#rdb" />
26
+
27
+ ### Options
28
+
29
+ <GeneratorParameters generator="ts#rdb" />
30
+
31
+ ## Generator Output
32
+
33
+ The generator will create the following project structure in the `<directory>/<name>` directory:
34
+
35
+ <FileTree>
36
+ - prisma
37
+ - models
38
+ - example.prisma Example model definition
39
+ - schema.prisma Main Prisma schema (references models)
40
+ - scripts
41
+ - docker-pull.ts Pulls the database Docker image for local development
42
+ - docker-start.ts Starts a local database container
43
+ - wait-for-db.ts Waits for the local database to be ready
44
+ - src
45
+ - index.ts Project entry point
46
+ - constants.ts Local development connection details and runtime config key
47
+ - prisma.ts Prisma runtime client wrapper
48
+ - utils.ts Runtime config and secret helpers
49
+ - create-db-user-handler.ts Lambda handler used to create the application database user during deployment
50
+ - migration-handler.ts Lambda handler used to run database migrations during deployment
51
+ - .gitignore Git ignore entries including generated Prisma client output
52
+ - Dockerfile Container image definition for the migration handler
53
+ - project.json Project configuration and build targets
54
+ - prisma.config.ts Configuration for Prisma CLI
55
+ </FileTree>
56
+
57
+ ### Infrastructure
58
+
59
+ <Snippet name="shared-constructs" />
60
+
61
+ <Infrastructure>
62
+ <Fragment slot="cdk">
63
+ <FileTree>
64
+ - packages/common/constructs/src
65
+ - app
66
+ - dbs
67
+ - \<name>.ts Infrastructure specific to your database
68
+ - core
69
+ - rdb
70
+ - aurora.ts Generic Aurora database construct
71
+ </FileTree>
72
+ </Fragment>
73
+ <Fragment slot="terraform">
74
+ <FileTree>
75
+ - packages/common/terraform/src
76
+ - app
77
+ - dbs
78
+ - \<name>
79
+ - \<name>.tf Module specific to your database
80
+ - core
81
+ - rdb
82
+ - aurora
83
+ - aurora.tf Generic Aurora module
84
+ </FileTree>
85
+ </Fragment>
86
+ </Infrastructure>
87
+
88
+ #### Architecture
89
+
90
+ The deployed database has the following architecture. By default, an [Amazon RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html) sits in front of the Aurora cluster to pool connections and to enable [IAM authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) — see [Disable RDS Proxy](#disable-rds-proxy) for the alternative. The architecture is the same whether you select the PostgreSQL or MySQL engine; only the Aurora engine flavor differs.
91
+
92
+ ```d2 inline=true
93
+ direction: right
94
+
95
+ app: Application\n(Lambda, Agent, ...) {
96
+ shape: hexagon
97
+ }
98
+
99
+ migrations: Migrations Lambda {
100
+ shape: image
101
+ icon: /nx-plugin-for-aws/icons/aws/lambda.svg
102
+ near: top-right
103
+ }
104
+
105
+ proxy: RDS Proxy {
106
+ shape: image
107
+ icon: /nx-plugin-for-aws/icons/aws/rds.svg
108
+ }
109
+
110
+ aurora: Aurora\n(PostgreSQL or MySQL) {
111
+ shape: image
112
+ icon: /nx-plugin-for-aws/icons/aws/aurora.svg
113
+ }
114
+
115
+ secrets: Secrets Manager\n(DB credentials) {
116
+ shape: image
117
+ icon: /nx-plugin-for-aws/icons/aws/secrets-manager.svg
118
+ near: bottom-right
119
+ }
120
+
121
+ app -> proxy: SQL (IAM auth)
122
+ proxy -> aurora
123
+ migrations -> aurora: Schema migrations
124
+ proxy -> secrets: Master credential rotation
125
+ ```
126
+
127
+ ## Local Development
128
+
129
+ ### Data Modelling
130
+
131
+ The generated project uses [Prisma ORM](https://www.prisma.io/docs/orm) to define your database schema and generate a type-safe client. The workflow is model-first: add or update Prisma model files under your database project's `prisma/models/` directory, then generate a migration from those model changes.
132
+
133
+ Example `User` model:
134
+
135
+ ```ts title="packages/postgres/prisma/models/user.prisma"
136
+ model User {
137
+ id Int @id @default(autoincrement())
138
+ firstName String
139
+ lastName String
140
+ }
141
+ ```
142
+
143
+ For more details, see the official [Prisma data modelling guide](https://www.prisma.io/docs/orm/core-concepts/data-modeling#data-modeling-with-prisma-orm).
144
+
145
+ ### Generating the Database Client
146
+
147
+ The generator automatically configures the `generate` target to create a type-safe TypeScript Prisma client whenever you build the project. The client is written to `generated/prisma` (added to `.gitignore`).
148
+
149
+ You can also manually generate the client at any time:
150
+
151
+ <NxCommands commands={['generate <your-db-project-name>']} />
152
+
153
+ Use the `prisma` target to run Prisma CLI commands from the workspace root:
154
+
155
+ <NxCommands commands={['run <project>:prisma generate']} />
156
+
157
+ The runtime wrapper in `src/prisma.ts` exports:
158
+
159
+ - `DB_PACKAGE_NAME` - the key used under the `database` runtime config namespace in AWS AppConfig
160
+ - `getPrisma()` - loads database connection settings from AWS AppConfig and creates a Prisma client using IAM authentication
161
+
162
+ The client automatically:
163
+ - Retrieves database configuration from AWS AppConfig using `RUNTIME_CONFIG_APP_ID` environment variable
164
+ - Generates temporary authentication tokens via AWS RDS Signer for IAM authentication
165
+ - Manages SSL/TLS connections with certificate validation
166
+ - Handles connection pooling through persistent database connection pools
167
+
168
+ ### Creating Migrations
169
+
170
+ After adding or updating models under `prisma/models/`, use `migrate dev` to generate migration files and apply them to your local database at the same time.
171
+
172
+ The generated `prisma` target automatically starts a local database via Docker before running:
173
+
174
+ <NxCommands commands={['run <project>:prisma migrate dev']} />
175
+
176
+ If you only want to generate the migration files without applying them to the local database, add `--create-only`:
177
+
178
+ <NxCommands commands={['run <project>:prisma migrate dev --create-only']} />
179
+
180
+ This generates a new migration folder in `prisma/migrations` each time your schema changes:
181
+
182
+ <FileTree>
183
+ - prisma
184
+ - migrations
185
+ - 20260405013911_initial_migrations
186
+ - migration.sql
187
+ - migration_lock.toml
188
+ - schema.prisma
189
+ </FileTree>
190
+
191
+ When you deploy the AWS stack, the generated infrastructure automatically applies the generated migrations to the deployed database.
192
+
193
+ ### Applying Existing Migrations
194
+
195
+ When you pull migration files created by other developers, use `migrate deploy` to apply those existing migrations to your local database.
196
+
197
+ <NxCommands commands={['run <project>:prisma migrate deploy']} />
198
+
199
+ In this local development flow, `migrate deploy` applies the migration files to your local database; it does not deploy the database to AWS.
200
+
201
+ ### Running Prisma Commands
202
+
203
+ The generated `prisma` target exposes the Prisma CLI, so you can use it to run any command supported by Prisma against the local database. See the [Prisma CLI reference](https://www.prisma.io/docs/orm/reference/prisma-cli-reference) for available commands.
204
+
205
+ <NxCommands commands={['run <project>:prisma <prisma-command>']} />
206
+
207
+ ### Prisma Studio
208
+
209
+ [Prisma Studio](https://www.prisma.io/studio) is a visual editor for your local database. Use it to browse tables, inspect and edit records, filter data, follow relations, and run raw SQL via the built-in SQL console. It is useful for verifying migrations and seeding test data during development. Launch it with:
210
+
211
+ <NxCommands commands={['run <project>:prisma studio']} />
212
+
213
+ ## Connecting to the Database
214
+
215
+ In any TypeScript project, import `getPrisma` from your database package and call it to get a type-safe Prisma client:
216
+
217
+ ```ts
218
+ import { getPrisma } from ':my-scope/db';
219
+
220
+ const prisma = await getPrisma();
221
+ const users = await prisma.user.findMany({ orderBy: { id: 'asc' } });
222
+ ```
223
+
224
+ `getPrisma()` returns a lazily-initialised, cached client. Subsequent calls within the same Lambda execution context reuse the existing connection pool rather than opening a new one.
225
+
226
+ The Prisma client exposes fully typed models derived from your `prisma/models/` schema, giving you end-to-end type safety from the database all the way through to your API response.
227
+
228
+ ### Connection Generators
229
+
230
+ For specific project types, use the `connection` generator to automatically wire up the database — it handles imports, context injection, and local development dependencies:
231
+
232
+ - <Link path="guides/connection/trpc-rdb">tRPC API → Relational Database</Link>
233
+ - <Link path="guides/connection/smithy-rdb">Smithy API → Relational Database</Link>
234
+ - <Link path="guides/connection/ts-agent-rdb">TypeScript Agent → Relational Database</Link>
235
+ - <Link path="guides/connection/ts-mcp-server-rdb">MCP Server → Relational Database</Link>
236
+
237
+ ## Deploying your Database
238
+
239
+ The relational database generator creates CDK or Terraform infrastructure based on your selected `iacProvider`.
240
+
241
+ <Infrastructure>
242
+ <Fragment slot="cdk">
243
+ The CDK construct is created in `common/constructs`. Example usage:
244
+
245
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
246
+ import { MyDatabase } from ':my-scope/common-constructs';
247
+
248
+ export class ApplicationStack extends Stack {
249
+ constructor(scope: Construct, id: string, props?: StackProps) {
250
+ super(scope, id, props);
251
+ ...
252
+ const db = new MyDatabase(this, 'Db', {
253
+ vpc,
254
+ vpcSubnets: {
255
+ subnetType: SubnetType.PRIVATE_ISOLATED,
256
+ }
257
+ });
258
+ }
259
+ }
260
+ ```
261
+
262
+ This provisions an Aurora cluster with RDS Proxy, admin credentials, application database user, runtime config registration, and migration handler.
263
+
264
+ The generated infrastructure creates two database users:
265
+ - **Admin user** - Created during cluster provisioning with credentials stored in AWS Secrets Manager
266
+ - **Application user** - Created via a Lambda custom resource with IAM authentication enabled and full privileges on the application database
267
+ </Fragment>
268
+ <Fragment slot="terraform">
269
+ The Terraform module is created in `common/terraform`. Example usage:
270
+
271
+ ```hcl title="packages/infra/src/main.tf"
272
+ module "my_database" {
273
+ source = "../../common/terraform/src/app/dbs/my-database"
274
+
275
+ vpc_id = module.vpc.vpc_id
276
+ database_subnet_ids = module.vpc.private_isolated_subnet_ids
277
+ lambda_subnet_ids = module.vpc.private_subnet_ids
278
+
279
+ tags = local.common_tags
280
+ }
281
+ ```
282
+
283
+ This provisions an Aurora cluster with RDS Proxy, admin credentials, create-db-user Lambda, runtime config registration, migration Lambda, and container registry resources.
284
+
285
+ The generated infrastructure creates two database users:
286
+ - **Admin user** - Created during cluster provisioning with credentials stored in AWS Secrets Manager
287
+ - **Application user** - Created via a Lambda function with IAM authentication enabled and full privileges on the application database
288
+ </Fragment>
289
+ </Infrastructure>
290
+
291
+ The application user is automatically created with a random name and IAM authentication. `getPrisma()` is already configured to authenticate as this user using short-lived RDS tokens, so your application code never handles database passwords.
292
+
293
+ Your VPC should include public subnets, private subnets with egress, and private isolated subnets. The database can run in private isolated subnets, while API Lambda functions should run in private subnets with egress so they can reach AWS services such as AppConfig.
294
+
295
+ <Infrastructure>
296
+ <Fragment slot="cdk">
297
+
298
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
299
+ const vpc = new Vpc(this, 'Vpc', {
300
+ subnetConfiguration: [
301
+ {
302
+ name: 'public',
303
+ subnetType: SubnetType.PUBLIC,
304
+ },
305
+ {
306
+ name: 'private_with_egress',
307
+ subnetType: SubnetType.PRIVATE_WITH_EGRESS,
308
+ },
309
+ {
310
+ name: 'private_isolated',
311
+ subnetType: SubnetType.PRIVATE_ISOLATED,
312
+ },
313
+ ],
314
+ });
315
+ ```
316
+
317
+ </Fragment>
318
+ <Fragment slot="terraform">
319
+
320
+ ```hcl title="packages/infra/src/main.tf"
321
+ module "vpc" {
322
+ source = "terraform-aws-modules/vpc/aws"
323
+ version = "~> 6.0"
324
+
325
+ name = "app"
326
+ ...
327
+ public_subnet_names = ["public"]
328
+ private_subnet_names = ["private_with_egress"]
329
+ intra_subnet_names = ["private_isolated"]
330
+
331
+ enable_nat_gateway = true
332
+ single_nat_gateway = true
333
+ }
334
+ ```
335
+
336
+ </Fragment>
337
+ </Infrastructure>
338
+
339
+ ### Connecting an API to the Database
340
+
341
+ <Infrastructure>
342
+ <Fragment slot="cdk">
343
+
344
+ In your application stack, deploy the API into the same VPC as the database, then call `allowDefaultPortFrom` and `grantConnect` to open the network path and grant IAM `rds-db:connect` permission to each Lambda handler:
345
+
346
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
347
+ import { MyDatabase } from ':my-scope/common-constructs';
348
+
349
+ const db = new MyDatabase(this, 'Db', { vpc, ... });
350
+
351
+ const api = new Api(this, 'Api', {
352
+ integrations: Api.defaultIntegrations(this)
353
+ .withDefaultOptions({
354
+ vpc,
355
+ vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
356
+ })
357
+ .build(),
358
+ });
359
+
360
+ Object.entries(api.integrations).forEach(([operation, integration]) => {
361
+ db.allowDefaultPortFrom(integration.handler, `Allow ${operation} to connect to the database`);
362
+ db.grantConnect(integration.handler);
363
+ });
364
+ ```
365
+
366
+ Deploy the API Lambda functions into a **private subnet with egress** (recommended) or a public subnet, not a private isolated subnet. At runtime, `getPrisma()` retrieves database connection details from AWS AppConfig, which is a public AWS service endpoint. Lambda functions in a private isolated subnet have no outbound internet access and cannot reach AppConfig. Private subnets with egress route outbound traffic through a NAT Gateway which sits in a public subnet.
367
+ </Fragment>
368
+ <Fragment slot="terraform">
369
+
370
+ Pass the database module outputs into your compute module so it can reach the database and read its runtime configuration:
371
+
372
+ ```hcl title="packages/infra/src/main.tf"
373
+ module "my_database" {
374
+ source = "../../common/terraform/src/app/dbs/my-database"
375
+ vpc_id = module.vpc.vpc_id
376
+ database_subnet_ids = module.vpc.private_isolated_subnet_ids
377
+ lambda_subnet_ids = module.vpc.private_subnet_ids
378
+ }
379
+
380
+ module "api" {
381
+ source = "..."
382
+ vpc_id = module.vpc.vpc_id
383
+ private_subnet_ids = module.vpc.private_subnet_ids
384
+
385
+ # Allow the API to read runtime config from AppConfig
386
+ appconfig_application_id = module.my_database.appconfig_application_id
387
+
388
+ # Grant rds-db:connect for the application database user
389
+ database_cluster_resource_id = module.my_database.cluster_resource_id
390
+ database_runtime_user = module.my_database.database_runtime_user
391
+
392
+ # Allow network access to the database port
393
+ database_security_group_id = module.my_database.security_group_id
394
+ database_port = module.my_database.cluster_port
395
+
396
+ environment_variables = {
397
+ RUNTIME_CONFIG_APP_ID = module.my_database.appconfig_application_id
398
+ }
399
+ }
400
+ ```
401
+
402
+ Deploy the API Lambda functions into **private subnets with egress** (recommended) or public subnets, not private isolated subnets. At runtime, `getPrisma()` retrieves database connection details from AWS AppConfig, which is a public AWS service endpoint. Lambda functions in a private isolated subnet have no outbound internet access and cannot reach AppConfig. Private subnets with egress route outbound traffic through a NAT Gateway which requires a public subnet.
403
+
404
+ Ensure the API Lambda role has `rds-db:connect` on `arn:aws:rds-db:<region>:<account>:dbuser:<cluster_resource_id>/<database_runtime_user>` and AppConfig read permissions, that the API's security group can reach the database security group on the database port, and that the Lambda environment includes `RUNTIME_CONFIG_APP_ID`.
405
+ </Fragment>
406
+ </Infrastructure>
407
+
408
+ :::note
409
+ This example grants every handler in your API access, but if only some handlers need access it's better to configure individually.
410
+ :::
411
+
412
+ ### RDS Proxy Configuration
413
+
414
+ The generated infrastructure includes an RDS Proxy by default, which sits between your application and the Aurora cluster. RDS Proxy provides several benefits:
415
+
416
+ - **Connection pooling** - Maintains a pool of database connections that can be shared across application instances, reducing the overhead of establishing new connections
417
+ - **Connection resilience** - Automatically handles failovers and reconnects during Aurora instance replacements or maintenance
418
+ - **IAM authentication** - Supports IAM-based database authentication, eliminating the need to manage database credentials in your application code
419
+ - **Improved security** - Enforces TLS encryption for all connections
420
+
421
+ :::note[Additional Cost]
422
+ RDS Proxy is enabled by default but incurs additional charges on top of the Aurora cluster cost. See [AWS RDS Proxy pricing](https://aws.amazon.com/rds/proxy/pricing/) for details. If you would prefer to disable the proxy, see the section below.
423
+ :::
424
+
425
+ #### Disable RDS Proxy
426
+
427
+ You can disable the RDS proxy as follows:
428
+
429
+ <Infrastructure>
430
+ <Fragment slot="cdk">
431
+
432
+
433
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
434
+ import { MyDatabase } from ':my-scope/common-constructs';
435
+
436
+ const db = new MyDatabase(this, 'Db', {
437
+ ...
438
+ enableRdsProxy: false,
439
+ });
440
+ ```
441
+
442
+ When RDS Proxy is disabled, your application connects directly to the Aurora cluster endpoint.
443
+ </Fragment>
444
+ <Fragment slot="terraform">
445
+
446
+ By default, RDS Proxy is enabled. The generated runtime client (`getPrisma()`) automatically connects through the proxy endpoint. You can disable it if needed:
447
+
448
+ ```hcl title="packages/infra/src/main.tf"
449
+ module "my_database" {
450
+ source = "../../common/terraform/src/app/dbs/my-database"
451
+ ...
452
+ enable_rds_proxy = false
453
+ }
454
+ ```
455
+
456
+ When RDS Proxy is disabled, your application connects directly to the Aurora cluster endpoint.
457
+ </Fragment>
458
+ </Infrastructure>
459
+
460
+ <Snippet name="connection/lambda-rdb-ssl-requirements" parentHeading="Disable RDS Proxy" />
461
+
462
+ The generated infrastructure can be customised to match your workload requirements. The following examples demonstrate a few common customisation options available.
463
+
464
+ ### Cluster Instances
465
+
466
+ Configure the writer and reader instances for your Aurora cluster.
467
+
468
+ <Infrastructure>
469
+ <Fragment slot="cdk">
470
+
471
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
472
+ import { MyDatabase } from ':my-scope/common-constructs';
473
+
474
+ const db = new MyDatabase(this, 'Db', {
475
+ ...
476
+ writer: ClusterInstance.serverlessV2('writer'),
477
+ readers: [ClusterInstance.serverlessV2('reader')],
478
+ });
479
+ ```
480
+ </Fragment>
481
+ <Fragment slot="terraform">
482
+
483
+ ```hcl title="packages/infra/src/main.tf"
484
+ module "my_database" {
485
+ source = "../../common/terraform/src/app/dbs/my-database"
486
+ ...
487
+ instance_count = 2 # 1 writer + 1 reader
488
+ }
489
+ ```
490
+ </Fragment>
491
+ </Infrastructure>
492
+
493
+ ### Serverless Capacity
494
+
495
+ Control Aurora Serverless v2 scaling limits to match your workload.
496
+
497
+ <Infrastructure>
498
+ <Fragment slot="cdk">
499
+
500
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
501
+ import { MyDatabase } from ':my-scope/common-constructs';
502
+
503
+ const db = new MyDatabase(this, 'Db', {
504
+ ...
505
+ serverlessV2MinCapacity: 0.5,
506
+ serverlessV2MaxCapacity: 8,
507
+ });
508
+ ```
509
+ </Fragment>
510
+ <Fragment slot="terraform">
511
+
512
+ ```hcl title="packages/infra/src/main.tf"
513
+ module "my_database" {
514
+ source = "../../common/terraform/src/app/dbs/my-database"
515
+ ...
516
+ serverless_min_capacity = 0.5
517
+ serverless_max_capacity = 8
518
+ }
519
+ ```
520
+ </Fragment>
521
+ </Infrastructure>
522
+
523
+ ### Engine Version
524
+
525
+ Pin a specific Aurora engine version.
526
+
527
+ By default, the generated local Docker database image matches the default Aurora engine version. If you change the Aurora engine version, it's recommended to also use a matching local Docker database version for maximum compatibility. See the AWS release notes for [Aurora PostgreSQL versions](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/aurorapostgresql-release-calendar.html) and [Aurora MySQL versions](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.Updates.30Updates.html) to identify the corresponding community database version.
528
+
529
+ The local database image is configured in the generated database project's `serve-local` target in `project.json`. Update the image argument passed to `scripts/docker-start.ts` when you change engine versions.
530
+
531
+ <OptionFilter when={{ engine: 'PostgreSQL' }}>
532
+ <Infrastructure>
533
+ <Fragment slot="cdk">
534
+
535
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
536
+ import { MyDatabase } from ':my-scope/common-constructs';
537
+
538
+ const db = new MyDatabase(this, 'Db', {
539
+ ...
540
+ engineVersion: AuroraPostgresEngineVersion.VER_17_7,
541
+ });
542
+ ```
543
+ </Fragment>
544
+ <Fragment slot="terraform">
545
+
546
+ ```hcl title="packages/infra/src/main.tf"
547
+ module "my_database" {
548
+ source = "../../common/terraform/src/app/dbs/my-database"
549
+ ...
550
+ engine_version = "17.7"
551
+ }
552
+ ```
553
+ </Fragment>
554
+ </Infrastructure>
555
+ </OptionFilter>
556
+
557
+ <OptionFilter when={{ engine: 'MySQL' }}>
558
+ <Infrastructure>
559
+ <Fragment slot="cdk">
560
+
561
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
562
+ import { MyDatabase } from ':my-scope/common-constructs';
563
+
564
+ const db = new MyDatabase(this, 'Db', {
565
+ ...
566
+ engineVersion: AuroraMysqlEngineVersion.VER_3_12_0,
567
+ });
568
+ ```
569
+ </Fragment>
570
+ <Fragment slot="terraform">
571
+
572
+ ```hcl title="packages/infra/src/main.tf"
573
+ module "my_database" {
574
+ source = "../../common/terraform/src/app/dbs/my-database"
575
+ ...
576
+ engine_version = "8.0.mysql_aurora.3.12.0"
577
+ }
578
+ ```
579
+ </Fragment>
580
+ </Infrastructure>
581
+ </OptionFilter>
582
+
583
+ ### Deletion Protection
584
+
585
+ Deletion protection is enabled by default (`deletionProtection: true` in CDK, `deletion_protection = true` in Terraform) to protect the Aurora cluster from accidental deletion.
586
+
587
+ #### Disable Deletion Protection
588
+
589
+ You can disable deletion protection for environments where database deletion is expected, such as short-lived development or preview stacks.
590
+
591
+ <Infrastructure>
592
+ <Fragment slot="cdk">
593
+
594
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
595
+ import { MyDatabase } from ':my-scope/common-constructs';
596
+
597
+ const db = new MyDatabase(this, 'Db', {
598
+ ...
599
+ deletionProtection: false,
600
+ });
601
+ ```
602
+ </Fragment>
603
+ <Fragment slot="terraform">
604
+
605
+ ```hcl title="packages/infra/src/main.tf"
606
+ module "my_database" {
607
+ source = "../../common/terraform/src/app/dbs/my-database"
608
+ ...
609
+ deletion_protection = false
610
+ }
611
+ ```
612
+ </Fragment>
613
+ </Infrastructure>
614
+
615
+ ### Removal Policy
616
+
617
+ The CDK construct retains the Aurora cluster by default (`removalPolicy: RemovalPolicy.RETAIN`). Change this when you want CDK stack deletion to snapshot or destroy the cluster instead.
618
+
619
+ When using `RemovalPolicy.DESTROY`, deletion protection must also be disabled before the cluster can be deleted.
620
+
621
+ <Infrastructure>
622
+ <Fragment slot="cdk">
623
+
624
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
625
+ import { RemovalPolicy } from 'aws-cdk-lib';
626
+ import { MyDatabase } from ':my-scope/common-constructs';
627
+
628
+ const db = new MyDatabase(this, 'Db', {
629
+ ...
630
+ removalPolicy: RemovalPolicy.SNAPSHOT,
631
+ });
632
+ ```
633
+
634
+ For an ephemeral environment where the database should be deleted with the stack:
635
+
636
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
637
+ import { RemovalPolicy } from 'aws-cdk-lib';
638
+ import { MyDatabase } from ':my-scope/common-constructs';
639
+
640
+ const db = new MyDatabase(this, 'Db', {
641
+ ...
642
+ deletionProtection: false,
643
+ removalPolicy: RemovalPolicy.DESTROY,
644
+ });
645
+ ```
646
+ </Fragment>
647
+ <Fragment slot="terraform">
648
+
649
+ Terraform does not use CDK removal policies. By default, the module creates a final snapshot on deletion (`skip_final_snapshot = false`). To skip the final snapshot for an ephemeral environment:
650
+
651
+ ```hcl title="packages/infra/src/main.tf"
652
+ module "my_database" {
653
+ source = "../../common/terraform/src/app/dbs/my-database"
654
+ ...
655
+ deletion_protection = false
656
+ skip_final_snapshot = true
657
+ }
658
+ ```
659
+ </Fragment>
660
+ </Infrastructure>
661
+
662
+ ### Encryption Key Rotation
663
+
664
+ The KMS key used to encrypt the Aurora cluster and its credentials secret has automatic key rotation enabled by default. Disable it if your security policy manages rotation externally.
665
+
666
+ <Infrastructure>
667
+ <Fragment slot="cdk">
668
+
669
+ ```ts title="packages/infra/src/stacks/application-stack.ts"
670
+ import { MyDatabase } from ':my-scope/common-constructs';
671
+
672
+ const db = new MyDatabase(this, 'Db', {
673
+ ...
674
+ enableKeyRotation: false,
675
+ });
676
+ ```
677
+ </Fragment>
678
+ <Fragment slot="terraform">
679
+
680
+ ```hcl title="packages/infra/src/main.tf"
681
+ module "my_database" {
682
+ source = "../../common/terraform/src/app/dbs/my-database"
683
+ ...
684
+ enable_key_rotation = false
685
+ }
686
+ ```
687
+ </Fragment>
688
+ </Infrastructure>
689
+
690
+ ## Limitations
691
+
692
+ <OptionFilter when={{ engine: 'MySQL' }}>
693
+
694
+ ### MySQL: API Gateway Streaming Mode
695
+
696
+ When using Aurora MySQL with API Gateway streaming responses (e.g. with tRPC's `httpBatchStreamLink`), the Prisma MySQL client holds onto the Node.js event loop after a query completes, preventing the Lambda from flushing the stream and ending the request.
697
+
698
+ To work around this, explicitly disconnect the client in a `finally` block after each query so the event loop is free to exit and the streaming response can complete.
699
+
700
+ **Option 1: per-procedure**
701
+
702
+ ```ts
703
+ export const listExampleTable = publicProcedure
704
+ .output(z.array(ExampleTableSchema))
705
+ .query(async () => {
706
+ const prisma = await getPrisma();
707
+ try {
708
+ return await prisma.exampleTable.findMany();
709
+ } finally {
710
+ await prisma.$disconnect();
711
+ }
712
+ });
713
+ ```
714
+
715
+ **Option 2: tRPC middleware**
716
+
717
+ If you are using the [middleware pattern](#injecting-the-prisma-client-via-middleware), add the `$disconnect()` call to the middleware so all procedures built on it are covered automatically:
718
+
719
+ ```ts title="packages/api/src/middleware/db.ts"
720
+ import { getPrisma } from ':my-scope/db';
721
+ import { initTRPC } from '@trpc/server';
722
+
723
+ export interface IDbContext {
724
+ db: Awaited<ReturnType<typeof getPrisma>>;
725
+ }
726
+
727
+ export const createDbPlugin = () => {
728
+ const t = initTRPC.context<IDbContext>().create();
729
+ return t.procedure.use(async (opts) => {
730
+ const db = await getPrisma();
731
+ try {
732
+ return await opts.next({
733
+ ctx: {
734
+ ...opts.ctx,
735
+ db,
736
+ },
737
+ });
738
+ } finally {
739
+ await db.$disconnect();
740
+ }
741
+ });
742
+ };
743
+ ```
744
+
745
+ :::note
746
+ This is not required for PostgreSQL. The PostgreSQL adapter uses a `pg` connection pool configured with `allowExitOnIdle: true`, which allows the event loop to exit naturally without an explicit disconnect. Due to this limitation, `getPrisma()` for MySQL does not cache the client or reuse connections across warm Lambda invocations. A new connection is opened on every call, unlike the PostgreSQL adapter which caches the client at the module level and reuses the same connection pool.
747
+ :::
748
+
749
+ ### MySQL: IAM Token Expiry
750
+
751
+ RDS IAM authentication tokens expire after **15 minutes**. The MySQL Prisma client captures the IAM token as a static value at the time `getPrisma()` is called. An existing open connection is not affected, but if a new connection needs to be established after the token has expired, authentication will fail. The PostgreSQL adapter avoids this by refreshing the token dynamically each time the pool opens a new connection, but the MySQL adapter has no equivalent mechanism.
752
+
753
+ :::caution
754
+ Be cautious with tasks running longer than 15 minutes. If the MySQL client needs to open a new connection after the original IAM token has expired, authentication will fail.
755
+ :::
756
+
757
+ For long-running tasks such as batch jobs or data migrations, call `getPrisma()` at the start of each unit of work rather than once for the entire operation. Because `getPrisma()` always creates a fresh client and fetches a new IAM token for MySQL, this ensures each connection authenticates with a valid token.
758
+
759
+ </OptionFilter>