@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,643 @@
1
+ ---
2
+ title: Type-Safe API Integrations
3
+ ---
4
+ import { Tabs, TabItem } from '@astrojs/starlight/components';
5
+ import Infrastructure from '@components/infrastructure.astro';
6
+
7
+ The REST/HTTP API CDK constructs are configured to provide a type-safe interface for defining integrations for each of your operations.
8
+
9
+ <Infrastructure>
10
+ <Fragment slot="cdk">
11
+ The CDK constructs provide full type-safe integration support as described below.
12
+ </Fragment>
13
+ <Fragment slot="terraform">
14
+ :::note[Router Pattern]
15
+ Terraform modules use the "router pattern" with a single Lambda function serving all operations. Type-safe integrations are not supported - the module creates one Lambda function that handles all API requests.
16
+
17
+ For explicit per-operation integrations with Terraform, you would need to manually create individual Lambda functions and API Gateway routes. See the [Explicit Integrations](#explicit-integrations) section for examples.
18
+ :::
19
+ </Fragment>
20
+ </Infrastructure>
21
+
22
+ #### Default Integrations
23
+
24
+ <Infrastructure>
25
+ <Fragment slot="cdk">
26
+ You can use the static `defaultIntegrations` to make use of the default pattern, which defines an individual AWS Lambda function for each operation:
27
+
28
+ ```ts {2}
29
+ new MyApi(this, 'MyApi', {
30
+ integrations: MyApi.defaultIntegrations(this).build(),
31
+ });
32
+ ```
33
+ </Fragment>
34
+ <Fragment slot="terraform">
35
+ Terraform modules automatically use the router pattern with a single Lambda function. No additional configuration is needed:
36
+
37
+ ```hcl
38
+ module "my_api" {
39
+ source = "../../common/terraform/src/app/apis/my-api"
40
+
41
+ asset_bucket_name = module.asset_bucket.bucket_name
42
+
43
+ # The module automatically creates a single Lambda function
44
+ # that handles all API operations
45
+ tags = local.common_tags
46
+ }
47
+ ```
48
+ </Fragment>
49
+ </Infrastructure>
50
+
51
+ #### Accessing Integrations
52
+
53
+ <Infrastructure>
54
+ <Fragment slot="cdk">
55
+ You can access the underlying AWS Lambda functions via the API construct's `integrations` property, in a type-safe manner. For example, if your API defines an operation named `sayHello` and you need to add some permissions to this function, you can do so as follows:
56
+
57
+ ```ts {6}
58
+ const api = new MyApi(this, 'MyApi', {
59
+ integrations: MyApi.defaultIntegrations(this).build(),
60
+ });
61
+
62
+ // sayHello is typed to the operations defined in your API
63
+ api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
64
+ effect: Effect.ALLOW,
65
+ actions: [...],
66
+ resources: [...],
67
+ }));
68
+ ```
69
+
70
+ If your API uses the `shared` pattern, the shared router Lambda is exposed as `api.integrations.$router`:
71
+
72
+ ```ts {6}
73
+ const api = new MyApi(this, 'MyApi', {
74
+ integrations: MyApi.defaultIntegrations(this).build(),
75
+ });
76
+
77
+ api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');
78
+ ```
79
+ </Fragment>
80
+ <Fragment slot="terraform">
81
+ With Terraform's router pattern, there's only one Lambda function. You can access it via the module outputs:
82
+
83
+ ```hcl
84
+ # Grant additional permissions to the single Lambda function
85
+ resource "aws_iam_role_policy" "additional_permissions" {
86
+ name = "additional-api-permissions"
87
+ role = module.my_api.lambda_execution_role_name
88
+
89
+ policy = jsonencode({
90
+ Version = "2012-10-17"
91
+ Statement = [
92
+ {
93
+ Effect = "Allow"
94
+ Action = [
95
+ "s3:GetObject",
96
+ "s3:PutObject"
97
+ ]
98
+ Resource = "arn:aws:s3:::my-bucket/*"
99
+ }
100
+ ]
101
+ })
102
+ }
103
+ ```
104
+ </Fragment>
105
+ </Infrastructure>
106
+
107
+ #### Customising Default Options
108
+
109
+ <Infrastructure>
110
+ <Fragment slot="cdk">
111
+ If you would like to customise the options used when creating the Lambda function for each default integration, you can use the `withDefaultOptions` method. For example, if you would like all of your Lambda functions to reside in a Vpc:
112
+
113
+ ```ts {5-7}
114
+ const vpc = new Vpc(this, 'Vpc', ...);
115
+
116
+ new MyApi(this, 'MyApi', {
117
+ integrations: MyApi.defaultIntegrations(this)
118
+ .withDefaultOptions({
119
+ vpc,
120
+ })
121
+ .build(),
122
+ });
123
+ ```
124
+
125
+
126
+ </Fragment>
127
+ <Fragment slot="terraform">
128
+ To customize options like VPC configuration, you need to edit the generated Terraform module. For example, to add VPC support to all Lambda functions:
129
+
130
+ ```hcl
131
+ # packages/common/terraform/src/app/apis/my-api/my-api.tf
132
+ # Add VPC variables
133
+ variable "vpc_subnet_ids" {
134
+ description = "List of VPC subnet IDs for Lambda function"
135
+ type = list(string)
136
+ default = []
137
+ }
138
+
139
+ variable "vpc_security_group_ids" {
140
+ description = "List of VPC security group IDs for Lambda function"
141
+ type = list(string)
142
+ default = []
143
+ }
144
+
145
+ # Update the Lambda function resource
146
+ resource "aws_lambda_function" "api_lambda" {
147
+ # ... existing configuration ...
148
+
149
+ # Add VPC configuration
150
+ vpc_config {
151
+ subnet_ids = var.vpc_subnet_ids
152
+ security_group_ids = var.vpc_security_group_ids
153
+ }
154
+ }
155
+ ```
156
+
157
+ Then use the module with VPC configuration:
158
+
159
+ ```hcl
160
+ module "my_api" {
161
+ source = "../../common/terraform/src/app/apis/my-api"
162
+
163
+ asset_bucket_name = module.asset_bucket.bucket_name
164
+
165
+ # VPC configuration
166
+ vpc_subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
167
+ vpc_security_group_ids = [aws_security_group.lambda_sg.id]
168
+
169
+ tags = local.common_tags
170
+ }
171
+ ```
172
+ </Fragment>
173
+ </Infrastructure>
174
+
175
+ #### Overriding Integrations
176
+
177
+ <Infrastructure>
178
+ <Fragment slot="cdk">
179
+ You can also override integrations for specific operations using the `withOverrides` method. Each override must specify an `integration` property which is typed to the appropriate CDK integration construct for the HTTP or REST API. The `withOverrides` method is also type-safe. For example, if you would like to override a `getDocumentation` API to point to documentation hosted by some external website you could achieve this as follows:
180
+
181
+ ```ts
182
+ new MyApi(this, 'MyApi', {
183
+ integrations: MyApi.defaultIntegrations(this)
184
+ .withOverrides({
185
+ getDocumentation: {
186
+ integration: new HttpIntegration('https://example.com/documentation'),
187
+ },
188
+ })
189
+ .build(),
190
+ });
191
+ ```
192
+
193
+ You will also notice that the overridden integration no longer has a `handler` property when accessing it via `api.integrations.getDocumentation`.
194
+
195
+ You can add additional properties to an integration which will also be typed accordingly, allowing for other types of integration to be abstracted but remain type-safe, for example if you have created an S3 integration for a REST API and later wish to reference the bucket for a particular operation, you can do so as follows:
196
+
197
+ ```ts {13, 41}
198
+ const storageBucket = new Bucket(this, 'Bucket', { ... });
199
+
200
+ const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', {
201
+ assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
202
+ });
203
+
204
+ storageBucket.grantRead(apiGatewayRole);
205
+
206
+ const api = new MyApi(this, 'MyApi', {
207
+ integrations: MyApi.defaultIntegrations(this)
208
+ .withOverrides({
209
+ getFile: {
210
+ bucket: storageBucket,
211
+ integration: new AwsIntegration({
212
+ service: 's3',
213
+ integrationHttpMethod: 'GET',
214
+ path: `${storageBucket.bucketName}/{fileName}`,
215
+ options: {
216
+ credentialsRole: apiGatewayRole,
217
+ requestParameters: {
218
+ 'integration.request.path.fileName': 'method.request.querystring.fileName',
219
+ },
220
+ integrationResponses: [{ statusCode: '200' }],
221
+ },
222
+ }),
223
+ options: {
224
+ requestParameters: {
225
+ 'method.request.querystring.fileName': true,
226
+ },
227
+ methodResponses: [{
228
+ statusCode: '200',
229
+ }],
230
+ }
231
+ },
232
+ })
233
+ .build(),
234
+ });
235
+
236
+ // Later, perhaps in another file, you can access the bucket property we defined
237
+ // in a type-safe manner
238
+ api.integrations.getFile.bucket.grantRead(...);
239
+ ```
240
+ </Fragment>
241
+ <Fragment slot="terraform">
242
+ :::note[Integration Overrides]
243
+ Overriding specific integrations is not supported with Terraform modules since they use the router pattern. All operations are handled by the single Lambda function.
244
+
245
+ For different integration types per operation, you would need to implement explicit integrations manually (see the [Explicit Integrations](#explicit-integrations) section below).
246
+ :::
247
+ </Fragment>
248
+ </Infrastructure>
249
+
250
+ #### Overriding Authorizers
251
+
252
+ <Infrastructure>
253
+ <Fragment slot="cdk">
254
+ You can also supply `options` in your integration to override particular method options such as authorizers, for example if you wished to use Cognito authentication for your `getDocumentation` operation:
255
+
256
+ ```ts
257
+ new MyApi(this, 'MyApi', {
258
+ integrations: MyApi.defaultIntegrations(this)
259
+ .withOverrides({
260
+ getDocumentation: {
261
+ integration: new HttpIntegration('https://example.com/documentation'),
262
+ options: {
263
+ authorizer: new CognitoUserPoolsAuthorizer(...) // for REST, or HttpUserPoolAuthorizer for an HTTP API
264
+ }
265
+ },
266
+ })
267
+ .build(),
268
+ });
269
+ ```
270
+ </Fragment>
271
+ <Fragment slot="terraform">
272
+ :::note[Authorizer Overrides]
273
+ Per-operation authorizer overrides are not supported with Terraform modules. The entire API uses the authentication method specified when generating the API (IAM, Cognito, or Custom).
274
+
275
+ For per-operation authorization, you would need to implement explicit integrations manually as below.
276
+ :::
277
+ </Fragment>
278
+ </Infrastructure>
279
+
280
+ #### Explicit Integrations
281
+
282
+ <Infrastructure>
283
+ <Fragment slot="cdk">
284
+ If you prefer, you can choose not to use the default integrations and instead directly supply one for each operation. This is useful if, for example, each operation needs to use a different type of integration or you would like to receive a type error when adding new operations:
285
+
286
+ ```ts
287
+ new MyApi(this, 'MyApi', {
288
+ integrations: {
289
+ sayHello: {
290
+ integration: new LambdaIntegration(...),
291
+ },
292
+ getDocumentation: {
293
+ integration: new HttpIntegration(...),
294
+ },
295
+ },
296
+ });
297
+ ```
298
+ </Fragment>
299
+ <Fragment slot="terraform">
300
+ For explicit per-operation integrations with Terraform, you should modify the generated app-specific module to replace the default proxy integration with specific integrations for each operation.
301
+
302
+ Edit `packages/common/terraform/src/app/apis/my-api/my-api.tf`:
303
+
304
+ 1. **Remove the default proxy routes** (e.g., `resource "aws_apigatewayv2_route" "proxy_routes"`)
305
+ 2. **Replace the single Lambda function** with individual functions for each operation
306
+ 3. **Create specific integrations and routes** for each operation, reusing the same ZIP bundle:
307
+
308
+ <Tabs syncKey="http-rest">
309
+ <TabItem label="HTTP API" _filter={{ computeType: 'ServerlessApiGatewayHttpApi' }}>
310
+
311
+ ```diff
312
+ # packages/common/terraform/src/app/apis/my-api/my-api.tf
313
+
314
+ # Remove the default single Lambda function
315
+ - resource "aws_lambda_function" "api_lambda" {
316
+ - filename = data.archive_file.lambda_zip.output_path
317
+ - function_name = "MyApiHandler"
318
+ - role = aws_iam_role.lambda_execution_role.arn
319
+ - handler = "index.handler"
320
+ - runtime = "nodejs22.x"
321
+ - timeout = 30
322
+ - # ... rest of configuration
323
+ - }
324
+
325
+ # Remove the default proxy integration
326
+ - resource "aws_apigatewayv2_integration" "lambda_integration" {
327
+ - api_id = module.http_api.api_id
328
+ - integration_type = "AWS_PROXY"
329
+ - integration_uri = aws_lambda_function.api_lambda.invoke_arn
330
+ - # ... rest of configuration
331
+ - }
332
+
333
+ # Remove the default proxy routes
334
+ - resource "aws_apigatewayv2_route" "proxy_routes" {
335
+ - for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
336
+ - api_id = module.http_api.api_id
337
+ - route_key = "${each.key} /{proxy+}"
338
+ - target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}"
339
+ - # ... rest of configuration
340
+ - }
341
+
342
+ # Add individual Lambda functions for each operation using the same bundle
343
+ + resource "aws_lambda_function" "say_hello_handler" {
344
+ + filename = data.archive_file.lambda_zip.output_path
345
+ + function_name = "MyApi-SayHello"
346
+ + role = aws_iam_role.lambda_execution_role.arn
347
+ + handler = "sayHello.handler" # Specific handler for this operation
348
+ + runtime = "nodejs22.x"
349
+ + timeout = 30
350
+ + source_code_hash = data.archive_file.lambda_zip.output_base64sha256
351
+ +
352
+ + tracing_config {
353
+ + mode = "Active"
354
+ + }
355
+ +
356
+ + environment {
357
+ + variables = var.env
358
+ + }
359
+ +
360
+ + tags = var.tags
361
+ + }
362
+
363
+ + resource "aws_lambda_function" "get_documentation_handler" {
364
+ + filename = data.archive_file.lambda_zip.output_path
365
+ + function_name = "MyApi-GetDocumentation"
366
+ + role = aws_iam_role.lambda_execution_role.arn
367
+ + handler = "getDocumentation.handler" # Specific handler for this operation
368
+ + runtime = "nodejs22.x"
369
+ + timeout = 30
370
+ + source_code_hash = data.archive_file.lambda_zip.output_base64sha256
371
+ +
372
+ + tracing_config {
373
+ + mode = "Active"
374
+ + }
375
+ +
376
+ + environment {
377
+ + variables = var.env
378
+ + }
379
+ +
380
+ + tags = var.tags
381
+ + }
382
+
383
+ # Add specific integrations for each operation
384
+ + resource "aws_apigatewayv2_integration" "say_hello_integration" {
385
+ + api_id = module.http_api.api_id
386
+ + integration_type = "AWS_PROXY"
387
+ + integration_uri = aws_lambda_function.say_hello_handler.invoke_arn
388
+ + payload_format_version = "2.0"
389
+ + timeout_milliseconds = 30000
390
+ + }
391
+
392
+ + resource "aws_apigatewayv2_integration" "get_documentation_integration" {
393
+ + api_id = module.http_api.api_id
394
+ + integration_type = "HTTP_PROXY"
395
+ + integration_uri = "https://example.com/documentation"
396
+ + integration_method = "GET"
397
+ + }
398
+
399
+ # Add specific routes for each operation
400
+ + resource "aws_apigatewayv2_route" "say_hello_route" {
401
+ + api_id = module.http_api.api_id
402
+ + route_key = "POST /sayHello"
403
+ + target = "integrations/${aws_apigatewayv2_integration.say_hello_integration.id}"
404
+ + authorization_type = "AWS_IAM"
405
+ + }
406
+
407
+ + resource "aws_apigatewayv2_route" "get_documentation_route" {
408
+ + api_id = module.http_api.api_id
409
+ + route_key = "GET /documentation"
410
+ + target = "integrations/${aws_apigatewayv2_integration.get_documentation_integration.id}"
411
+ + authorization_type = "NONE"
412
+ + }
413
+
414
+ # Add Lambda permissions for each function
415
+ + resource "aws_lambda_permission" "say_hello_permission" {
416
+ + statement_id = "AllowExecutionFromAPIGateway-SayHello"
417
+ + action = "lambda:InvokeFunction"
418
+ + function_name = aws_lambda_function.say_hello_handler.function_name
419
+ + principal = "apigateway.amazonaws.com"
420
+ + source_arn = "${module.http_api.api_execution_arn}/*/*"
421
+ + }
422
+
423
+ + resource "aws_lambda_permission" "get_documentation_permission" {
424
+ + statement_id = "AllowExecutionFromAPIGateway-GetDocumentation"
425
+ + action = "lambda:InvokeFunction"
426
+ + function_name = aws_lambda_function.get_documentation_handler.function_name
427
+ + principal = "apigateway.amazonaws.com"
428
+ + source_arn = "${module.http_api.api_execution_arn}/*/*"
429
+ + }
430
+ ```
431
+ </TabItem>
432
+ <TabItem label="REST API" _filter={{ computeType: 'ServerlessApiGatewayRestApi' }}>
433
+
434
+ ```diff
435
+ # packages/common/terraform/src/app/apis/my-api/my-api.tf
436
+
437
+ # Remove the default single Lambda function
438
+ - resource "aws_lambda_function" "api_lambda" {
439
+ - filename = data.archive_file.lambda_zip.output_path
440
+ - function_name = "MyApiHandler"
441
+ - role = aws_iam_role.lambda_execution_role.arn
442
+ - handler = "index.handler"
443
+ - runtime = "nodejs22.x"
444
+ - timeout = 30
445
+ - # ... rest of configuration
446
+ - }
447
+
448
+ # Remove the default proxy integration
449
+ - resource "aws_apigatewayv2_integration" "lambda_integration" {
450
+ - api_id = module.http_api.api_id
451
+ - integration_type = "AWS_PROXY"
452
+ - integration_uri = aws_lambda_function.api_lambda.invoke_arn
453
+ - # ... rest of configuration
454
+ - }
455
+
456
+ # Remove the default proxy routes
457
+ - resource "aws_apigatewayv2_route" "proxy_routes" {
458
+ - for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
459
+ - api_id = module.http_api.api_id
460
+ - route_key = "${each.key} /{proxy+}"
461
+ - target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}"
462
+ - # ... rest of configuration
463
+ - }
464
+
465
+ # Add individual Lambda functions for each operation using the same bundle
466
+ + resource "aws_lambda_function" "say_hello_handler" {
467
+ + filename = data.archive_file.lambda_zip.output_path
468
+ + function_name = "MyApi-SayHello"
469
+ + role = aws_iam_role.lambda_execution_role.arn
470
+ + handler = "sayHello.handler" # Specific handler for this operation
471
+ + runtime = "nodejs22.x"
472
+ + timeout = 30
473
+ + source_code_hash = data.archive_file.lambda_zip.output_base64sha256
474
+ +
475
+ + tracing_config {
476
+ + mode = "Active"
477
+ + }
478
+ +
479
+ + environment {
480
+ + variables = var.env
481
+ + }
482
+ +
483
+ + tags = var.tags
484
+ + }
485
+
486
+ + resource "aws_lambda_function" "get_documentation_handler" {
487
+ + filename = data.archive_file.lambda_zip.output_path
488
+ + function_name = "MyApi-GetDocumentation"
489
+ + role = aws_iam_role.lambda_execution_role.arn
490
+ + handler = "getDocumentation.handler" # Specific handler for this operation
491
+ + runtime = "nodejs22.x"
492
+ + timeout = 30
493
+ + source_code_hash = data.archive_file.lambda_zip.output_base64sha256
494
+ +
495
+ + tracing_config {
496
+ + mode = "Active"
497
+ + }
498
+ +
499
+ + environment {
500
+ + variables = var.env
501
+ + }
502
+ +
503
+ + tags = var.tags
504
+ + }
505
+
506
+ # Add specific resources and methods for each operation
507
+ + resource "aws_api_gateway_resource" "say_hello_resource" {
508
+ + rest_api_id = module.rest_api.api_id
509
+ + parent_id = module.rest_api.api_root_resource_id
510
+ + path_part = "sayHello"
511
+ + }
512
+
513
+ + resource "aws_api_gateway_method" "say_hello_method" {
514
+ + rest_api_id = module.rest_api.api_id
515
+ + resource_id = aws_api_gateway_resource.say_hello_resource.id
516
+ + http_method = "POST"
517
+ + authorization = "AWS_IAM"
518
+ + }
519
+
520
+ + resource "aws_api_gateway_integration" "say_hello_integration" {
521
+ + rest_api_id = module.rest_api.api_id
522
+ + resource_id = aws_api_gateway_resource.say_hello_resource.id
523
+ + http_method = aws_api_gateway_method.say_hello_method.http_method
524
+ +
525
+ + integration_http_method = "POST"
526
+ + type = "AWS_PROXY"
527
+ + uri = aws_lambda_function.say_hello_handler.invoke_arn
528
+ + }
529
+
530
+ + resource "aws_api_gateway_resource" "get_documentation_resource" {
531
+ + rest_api_id = module.rest_api.api_id
532
+ + parent_id = module.rest_api.api_root_resource_id
533
+ + path_part = "documentation"
534
+ + }
535
+
536
+ + resource "aws_api_gateway_method" "get_documentation_method" {
537
+ + rest_api_id = module.rest_api.api_id
538
+ + resource_id = aws_api_gateway_resource.get_documentation_resource.id
539
+ + http_method = "GET"
540
+ + authorization = "NONE"
541
+ + }
542
+
543
+ + resource "aws_api_gateway_integration" "get_documentation_integration" {
544
+ + rest_api_id = module.rest_api.api_id
545
+ + resource_id = aws_api_gateway_resource.get_documentation_resource.id
546
+ + http_method = aws_api_gateway_method.get_documentation_method.http_method
547
+ +
548
+ + integration_http_method = "GET"
549
+ + type = "HTTP"
550
+ + uri = "https://example.com/documentation"
551
+ + }
552
+
553
+ # Update deployment to depend on new integrations
554
+ ~ resource "aws_api_gateway_deployment" "api_deployment" {
555
+ rest_api_id = module.rest_api.api_id
556
+
557
+ depends_on = [
558
+ - aws_api_gateway_integration.lambda_integration,
559
+ + aws_api_gateway_integration.say_hello_integration,
560
+ + aws_api_gateway_integration.get_documentation_integration,
561
+ ]
562
+
563
+ lifecycle {
564
+ create_before_destroy = true
565
+ }
566
+
567
+ + triggers = {
568
+ + redeployment = sha1(jsonencode([
569
+ + aws_api_gateway_integration.say_hello_integration,
570
+ + aws_api_gateway_integration.get_documentation_integration,
571
+ + ]))
572
+ + }
573
+ }
574
+
575
+ # Add Lambda permissions for each function
576
+ + resource "aws_lambda_permission" "say_hello_permission" {
577
+ + statement_id = "AllowExecutionFromAPIGateway-SayHello"
578
+ + action = "lambda:InvokeFunction"
579
+ + function_name = aws_lambda_function.say_hello_handler.function_name
580
+ + principal = "apigateway.amazonaws.com"
581
+ + source_arn = "${module.rest_api.api_execution_arn}/*/*"
582
+ + }
583
+
584
+ + resource "aws_lambda_permission" "get_documentation_permission" {
585
+ + statement_id = "AllowExecutionFromAPIGateway-GetDocumentation"
586
+ + action = "lambda:InvokeFunction"
587
+ + function_name = aws_lambda_function.get_documentation_handler.function_name
588
+ + principal = "apigateway.amazonaws.com"
589
+ + source_arn = "${module.rest_api.api_execution_arn}/*/*"
590
+ + }
591
+ ```
592
+ </TabItem>
593
+ </Tabs>
594
+
595
+ </Fragment>
596
+ </Infrastructure>
597
+
598
+ #### Integration Pattern
599
+
600
+ <Infrastructure>
601
+ <Fragment slot="cdk">
602
+
603
+ Generated CDK API constructs support two integration patterns:
604
+
605
+ - `isolated` creates one Lambda function per operation. This is the default for generated APIs.
606
+ - `shared` creates a single default router Lambda and reuses it for every operation unless you override specific integrations.
607
+
608
+ `isolated` gives you finer-grained permissions and configuration per operation. `shared` reduces Lambda and API Gateway integration sprawl while still allowing selective overrides.
609
+
610
+ For example, setting `pattern` to `'shared'` creates a single function instead of one per integration:
611
+
612
+ ```ts {5,11-12}
613
+ // packages/common/constructs/src/app/apis/my-api.ts
614
+ export class MyApi<...> extends ... {
615
+
616
+ public static defaultIntegrations = (scope: Construct) => {
617
+ ...
618
+ return IntegrationBuilder.rest({
619
+ pattern: 'shared',
620
+ ...
621
+ });
622
+ };
623
+ }
624
+ ```
625
+ </Fragment>
626
+ <Fragment slot="terraform">
627
+ Terraform modules automatically use the router pattern - this is the default and only supported approach. The generated module creates a single Lambda function that handles all API operations.
628
+
629
+ You can simply instantiate the default module to get the router pattern:
630
+
631
+ ```hcl
632
+ # Default router pattern - single Lambda function for all operations
633
+ module "my_api" {
634
+ source = "../../common/terraform/src/app/apis/my-api"
635
+
636
+ asset_bucket_name = module.asset_bucket.bucket_name
637
+
638
+ # Single Lambda function handles all operations automatically
639
+ tags = local.common_tags
640
+ }
641
+ ```
642
+ </Fragment>
643
+ </Infrastructure>
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: WAF configuration
3
+ ---
4
+ import Infrastructure from '@components/infrastructure.astro';
5
+
6
+ For REST APIs, the generated construct associates an [AWS WAFv2](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html) Web ACL with the API Gateway stage by default. The Web ACL uses the AWS managed default ruleset ([`AWSManagedRulesCommonRuleSet`](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-crs) and [`AWSManagedRulesKnownBadInputsRuleSet`](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-known-bad-inputs)), providing protection against common web exploits including the OWASP Top 10. WAF request logs are written to a CloudWatch Logs group.
7
+
8
+ :::caution[SizeRestrictions_BODY deviation from defaults]
9
+ The `SizeRestrictions_BODY` rule from `AWSManagedRulesCommonRuleSet` is overridden to `Count` rather than `Block`, since the rule's 8 KB limit is too restrictive for most APIs. Oversized requests will still be recorded as metrics so you can monitor them. See the [AWS WAF body size limits](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) guide for more details.
10
+ :::
11
+
12
+ You can edit the generated rest-api construct to add, remove, or adjust rules (for example, to add [rate-based rules](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html) or additional managed rule groups).
13
+
14
+ <Infrastructure>
15
+ <Fragment slot="cdk">
16
+ To opt out (for example, to attach your own Web ACL), set `enableWaf` to `false`:
17
+
18
+ ```ts {4}
19
+ const api = new MyApi(this, 'MyApi', {
20
+ integrations: MyApi.defaultIntegrations(this).build(),
21
+ enableWaf: false,
22
+ });
23
+ ```
24
+ </Fragment>
25
+ <Fragment slot="terraform">
26
+ To opt out (for example, to attach your own Web ACL), set `enable_waf` to `false`:
27
+
28
+ ```hcl {4-5}
29
+ module "my_api" {
30
+ source = "../../common/terraform/src/app/apis/my-api"
31
+
32
+ asset_bucket_name = module.asset_bucket.bucket_name
33
+ enable_waf = false
34
+ }
35
+ ```
36
+ </Fragment>
37
+ </Infrastructure>