@aws/nx-plugin 0.79.1 → 0.80.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.
@@ -1,15 +1,16 @@
1
+ import json
1
2
  import os
2
3
  import uuid
3
- from collections.abc import Callable
4
+ from collections.abc import AsyncIterator, Callable
5
+ from typing import Any
4
6
  from urllib.parse import urlparse
5
7
 
6
8
  from aws_lambda_powertools import Logger, Metrics, Tracer
7
9
  from aws_lambda_powertools.metrics import MetricUnit
8
10
  from fastapi import FastAPI, Request, Response
9
11
  from fastapi.openapi.utils import get_openapi
10
- from fastapi.responses import JSONResponse
12
+ from fastapi.responses import JSONResponse, StreamingResponse
11
13
  from fastapi.routing import APIRoute
12
- from mangum import Mangum
13
14
  from pydantic import BaseModel
14
15
  from starlette.middleware.exceptions import ExceptionMiddleware
15
16
 
@@ -25,16 +26,54 @@ class InternalServerErrorDetails(BaseModel):
25
26
  detail: str
26
27
 
27
28
 
28
- app = FastAPI(title="<%= apiNameClassName %>", responses={500: {"model": InternalServerErrorDetails}})
29
- lambda_handler = Mangum(app)
29
+ class JsonStreamingResponse(StreamingResponse):
30
+ """A streaming response that serializes items to JSON Lines format."""
31
+
32
+ media_type = "application/jsonl"
33
+
34
+ def __init__(
35
+ self,
36
+ content: AsyncIterator[BaseModel],
37
+ status_code: int = 200,
38
+ headers: dict[str, str] | None = None,
39
+ **kwargs: Any,
40
+ ) -> None:
41
+ """Stream json lines from an async iterator yielding Pydantic models"""
42
+ super().__init__(
43
+ content=self._serialize(content),
44
+ status_code=status_code,
45
+ headers=headers,
46
+ media_type=self.media_type,
47
+ **kwargs,
48
+ )
49
+
50
+ @staticmethod
51
+ async def _serialize(
52
+ content: AsyncIterator[BaseModel],
53
+ ) -> AsyncIterator[bytes]:
54
+ """Serialize Pydantic models to JSON Lines format."""
55
+ async for item in content:
56
+ yield (item.model_dump_json() + "\n").encode("utf-8")
57
+
58
+ @staticmethod
59
+ def openapi_response(
60
+ item_model: type[BaseModel],
61
+ description: str = "Streaming response",
62
+ ) -> dict[str, Any]:
63
+ """Generate an OpenAPI application/jsonl response for a stream of the given model"""
64
+ return {
65
+ "description": description,
66
+ "content": {
67
+ "application/jsonl": {
68
+ "itemSchema": {"$ref": f"#/components/schemas/{item_model.__name__}"},
69
+ }
70
+ },
71
+ # Include the model so FastAPI registers the schema in components/schemas
72
+ "model": item_model,
73
+ }
74
+
30
75
 
31
- # Add tracing
32
- lambda_handler.__name__ = "handler" # tracer requires __name__ to be set
33
- lambda_handler = tracer.capture_lambda_handler(lambda_handler)
34
- # Add logging
35
- lambda_handler = logger.inject_lambda_context(lambda_handler, clear_state=True)
36
- # Add metrics last to properly flush metrics.
37
- lambda_handler = metrics.log_metrics(lambda_handler, capture_cold_start_metric=True)
76
+ app = FastAPI(title="<%= apiNameClassName %>", responses={500: {"model": InternalServerErrorDetails}})
38
77
 
39
78
 
40
79
  # Add cors middleware
@@ -92,10 +131,16 @@ async def metrics_handler(request: Request, call_next):
92
131
  async def add_correlation_id(request: Request, call_next):
93
132
  # Get correlation id from X-Correlation-Id header
94
133
  corr_id = request.headers.get("x-correlation-id")
95
- if not corr_id and "aws.context" in request.scope:
96
- # If empty, use request id from aws context
97
- corr_id = request.scope["aws.context"].aws_request_id
98
- elif not corr_id:
134
+ if not corr_id:
135
+ # Try to get request id from Lambda context (forwarded by Lambda Web Adapter)
136
+ lambda_context_header = request.headers.get("x-amzn-lambda-context")
137
+ if lambda_context_header:
138
+ try:
139
+ lambda_context = json.loads(lambda_context_header)
140
+ corr_id = lambda_context.get("request_id")
141
+ except (json.JSONDecodeError, KeyError):
142
+ pass
143
+ if not corr_id:
99
144
  # If still empty, use uuid
100
145
  corr_id = uuid.uuid4().hex
101
146
 
@@ -1,8 +1,7 @@
1
+ import uvicorn
1
2
  from pydantic import BaseModel
2
3
 
3
- from .init import app, lambda_handler, tracer
4
-
5
- handler = lambda_handler
4
+ from .init import app, tracer
6
5
 
7
6
 
8
7
  class EchoOutput(BaseModel):
@@ -13,3 +12,7 @@ class EchoOutput(BaseModel):
13
12
  @tracer.capture_method
14
13
  def echo(message: str) -> EchoOutput:
15
14
  return EchoOutput(message=f"{message}")
15
+
16
+
17
+ if __name__ == "__main__":
18
+ uvicorn.run("<%- name %>.main:app", port=8000)
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ exec python -m uvicorn <%= name %>.main:app --host 0.0.0.0 --port ${PORT:-8000}
@@ -16,6 +16,7 @@ const object_1 = require("../../utils/object");
16
16
  const nx_1 = require("../../utils/nx");
17
17
  const metrics_1 = require("../../utils/metrics");
18
18
  const api_constructs_1 = require("../../utils/api-constructs/api-constructs");
19
+ const fs_1 = require("../../utils/fs");
19
20
  const open_api_1 = require("./react/open-api");
20
21
  const port_1 = require("../../utils/port");
21
22
  const bundle_1 = require("../../utils/bundle/bundle");
@@ -46,7 +47,14 @@ const pyFastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, vo
46
47
  });
47
48
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, fullyQualifiedName);
48
49
  const port = (0, port_1.assignPort)(tree, projectConfig, 8000);
49
- const { bundleOutputDir } = (0, bundle_1.addPythonBundleTarget)(projectConfig);
50
+ const { bundleOutputDir, bundleTargetName } = (0, bundle_1.addPythonBundleTarget)(projectConfig);
51
+ // Add a command to copy run.sh to the bundle output for Lambda Web Adapter
52
+ const fs = new fs_1.FsCommands(tree);
53
+ const bundleTarget = projectConfig.targets[bundleTargetName];
54
+ bundleTarget.options.commands = [
55
+ ...bundleTarget.options.commands,
56
+ fs.cp(`{projectRoot}/run.sh`, `dist/{projectRoot}/${bundleTargetName}/run.sh`),
57
+ ];
50
58
  projectConfig.targets.serve = {
51
59
  executor: '@nxlv/python:run-commands',
52
60
  options: {
@@ -96,7 +104,7 @@ const pyFastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, vo
96
104
  });
97
105
  (0, py_1.addDependenciesToPyProjectToml)(tree, dir, [
98
106
  'fastapi',
99
- 'mangum',
107
+ 'uvicorn',
100
108
  'aws-lambda-powertools',
101
109
  'aws-lambda-powertools[tracer]',
102
110
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/py/fast-api/generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/py/fast-api/generator.ts"],"names":[],"mappings":";;;;AAAA;;;GAGG;AACH,uCAUoB;AAEpB,yDAA6D;AAC7D,0EAA+E;AAC/E,qEAA0E;AAC1E,6CAA6D;AAC7D,+CAA0D;AAC1D,+CAAoD;AACpD,uCAIwB;AACxB,iDAAsE;AACtE,8EAA+E;AAC/E,uCAA4C;AAC5C,+CAAwD;AACxD,2CAA8C;AAC9C,sDAAkE;AAClE,uCAGwB;AACxB,yCAAqD;AACrD,oFAAgH;AAEnG,QAAA,uBAAuB,GAClC,IAAA,qBAAgB,EAAC,UAAU,CAAC,CAAC;AAE/B;;GAEG;AACI,MAAM,yBAAyB,GAAG,CACvC,IAAU,EACV,MAAuC,EACX,EAAE;IAC9B,MAAM,WAAW,GAAG,MAAM,IAAA,wBAAkB,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAEvE,MAAM,IAAA,6CAAyB,EAAC,IAAI,EAAE;QACpC,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,EAAE,GAAG,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,GAAG,IAAA,+BAAmB,EAC3E,IAAI,EACJ;QACE,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CACF,CAAC;IACF,MAAM,gBAAgB,GAAG,IAAA,mBAAW,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,gBAAgB,GAAG,IAAA,mBAAW,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAElD,MAAM,IAAA,mBAAkB,EAAC,IAAI,EAAE;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,oBAAoB;QAChC,WAAW,EAAE,aAAa;KAC3B,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,IAAA,iBAAU,EAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IAEnD,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,GACzC,IAAA,8BAAqB,EAAC,aAAa,CAAC,CAAC;IAEvC,2EAA2E;IAC3E,MAAM,EAAE,GAAG,IAAI,eAAU,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7D,YAAY,CAAC,OAAO,CAAC,QAAQ,GAAG;QAC9B,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ;QAChC,EAAE,CAAC,EAAE,CACH,sBAAsB,EACtB,sBAAsB,gBAAgB,SAAS,CAChD;KACF,CAAC;IAEF,aAAa,CAAC,OAAO,CAAC,KAAK,GAAG;QAC5B,QAAQ,EAAE,2BAA2B;QACrC,OAAO,EAAE;YACP,OAAO,EAAE,sBAAsB,oBAAoB,mBAAmB,IAAI,EAAE;YAC5E,GAAG,EAAE,eAAe;SACrB;QACD,UAAU,EAAE,IAAI;KACjB,CAAC;IAEF,aAAa,CAAC,QAAQ,GAAG,gCACpB,aAAa,CAAC,QAAQ,KACzB,OAAO,EAAE,MAAM,CAAC,IAAI,EACpB,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,MAAM,CAAC,IAAI,GACX,CAAC;IAET,aAAa,CAAC,OAAO,GAAG,IAAA,uBAAc,EAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAA,mCAA0B,EAAC,IAAI,EAAE,kBAAkB,EAAE,aAAa,CAAC,CAAC;IAEpE,mEAAmE;IACnE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,+BAAoB,EAAC,IAAI,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAE5E;QACE,IAAA,0BAAiB,EAAC,GAAG,EAAE,oBAAoB,EAAE,UAAU,CAAC;QACxD,IAAA,0BAAiB,EAAC,GAAG,EAAE,OAAO,EAAE,eAAe,CAAC;KACjD,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjC,IAAA,sBAAa,EACX,IAAI,EAAE,0BAA0B;IAChC,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,6BAA6B;IAC3E,GAAG,EAAE,gCAAgC;IACrC;QACE,IAAI,EAAE,oBAAoB;QAC1B,gBAAgB;QAChB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,EACD;QACE,iBAAiB,EAAE,0BAAiB,CAAC,SAAS;KAC/C,CACF,CAAC;IAEF,mEAAmE;IACnE,IAAA,mCAAkB,EAAC,IAAI,EAAE;QACvB,cAAc,EAAE,aAAa,CAAC,IAAI;QAClC,gBAAgB;QAChB,gBAAgB;QAChB,aAAa,EACX,MAAM,CAAC,WAAW,KAAK,6BAA6B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;QACxE,OAAO,EAAE;YACP,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,oBAAoB;YAChC,eAAe;SAChB;QACD,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW;KACZ,CAAC,CAAC;IAEH,IAAA,oEAAgD,EAAC,IAAI,EAAE;QACrD,WAAW;QACX,gBAAgB;QAChB,QAAQ;QACR,mBAAmB,EAAE,GAAG,aAAa,CAAC,IAAI,UAAU;KACrD,CAAC,CAAC;IAEH,IAAA,mCAA8B,EAAC,IAAI,EAAE,GAAG,EAAE;QACxC,SAAS;QACT,SAAS;QACT,uBAAuB;QACvB,+BAA+B;KAChC,CAAC,CAAC;IACH,IAAA,oDAA+C,EAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE;QAChE,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAA,yBAAoB,EAAC,IAAI,EAAE,kBAAkB,EAAE,+BAAuB,CAAC,CAAC;IAExE,MAAM,IAAA,yCAA+B,EAAC,IAAI,EAAE,CAAC,+BAAuB,CAAC,CAAC,CAAC;IAEvE,MAAM,IAAA,6BAAoB,EAAC,IAAI,CAAC,CAAC;IAEjC,OAAO,GAAS,EAAE;QAChB,MAAM,IAAI,wBAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,oBAAM,EAAE,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9D,IAAA,4BAAmB,EAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAA,CAAC;AACJ,CAAC,CAAA,CAAC;AAjIW,QAAA,yBAAyB,6BAiIpC;AACF,kBAAe,iCAAyB,CAAC"}
@@ -643,6 +643,7 @@ resource "aws_lambda_function" "api_lambda" {
643
643
  mode = "Active"
644
644
  }
645
645
 
646
+
646
647
  environment {
647
648
  variables = merge({
648
649
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -706,6 +707,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
706
707
  }
707
708
 
708
709
 
710
+
709
711
  # Create proxy resource (captures all paths)
710
712
  resource "aws_api_gateway_resource" "proxy_resource" {
711
713
  rest_api_id = module.rest_api.api_id
@@ -867,6 +869,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
867
869
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
868
870
  }
869
871
 
872
+
870
873
  # Add API url to runtime config
871
874
  module "add_url_to_runtime_config" {
872
875
  source = "../../../core/runtime-config/entry"
@@ -1666,6 +1669,7 @@ resource "aws_lambda_function" "api_lambda" {
1666
1669
  mode = "Active"
1667
1670
  }
1668
1671
 
1672
+
1669
1673
  environment {
1670
1674
  variables = merge({
1671
1675
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -1728,6 +1732,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
1728
1732
  tags = var.tags
1729
1733
  }
1730
1734
 
1735
+
1731
1736
  # Cognito User Pool Authorizer
1732
1737
  resource "aws_api_gateway_authorizer" "cognito_authorizer" {
1733
1738
  name = "TestApiAuthorizer"
@@ -1897,6 +1902,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
1897
1902
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
1898
1903
  }
1899
1904
 
1905
+
1900
1906
  # Add API url to runtime config
1901
1907
  module "add_url_to_runtime_config" {
1902
1908
  source = "../../../core/runtime-config/entry"
@@ -2292,6 +2298,7 @@ resource "aws_lambda_function" "api_lambda" {
2292
2298
  mode = "Active"
2293
2299
  }
2294
2300
 
2301
+
2295
2302
  environment {
2296
2303
  variables = merge({
2297
2304
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -2355,6 +2362,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
2355
2362
  }
2356
2363
 
2357
2364
 
2365
+
2358
2366
  # Create proxy resource (captures all paths)
2359
2367
  resource "aws_api_gateway_resource" "proxy_resource" {
2360
2368
  rest_api_id = module.rest_api.api_id
@@ -2525,6 +2533,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
2525
2533
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
2526
2534
  }
2527
2535
 
2536
+
2528
2537
  # Add API url to runtime config
2529
2538
  module "add_url_to_runtime_config" {
2530
2539
  source = "../../../core/runtime-config/entry"
@@ -2769,6 +2778,7 @@ resource "aws_lambda_function" "api_lambda" {
2769
2778
  mode = "Active"
2770
2779
  }
2771
2780
 
2781
+
2772
2782
  environment {
2773
2783
  variables = merge({
2774
2784
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -2832,6 +2842,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
2832
2842
  }
2833
2843
 
2834
2844
 
2845
+
2835
2846
  # Create proxy resource (captures all paths)
2836
2847
  resource "aws_api_gateway_resource" "proxy_resource" {
2837
2848
  rest_api_id = module.rest_api.api_id
@@ -2993,6 +3004,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
2993
3004
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
2994
3005
  }
2995
3006
 
3007
+
2996
3008
  # Add API url to runtime config
2997
3009
  module "add_url_to_runtime_config" {
2998
3010
  source = "../../../core/runtime-config/entry"
@@ -2709,6 +2709,7 @@ resource "aws_lambda_function" "api_lambda" {
2709
2709
  mode = "Active"
2710
2710
  }
2711
2711
 
2712
+
2712
2713
  environment {
2713
2714
  variables = merge({
2714
2715
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -2771,6 +2772,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
2771
2772
  tags = var.tags
2772
2773
  }
2773
2774
 
2775
+
2774
2776
  # Cognito User Pool Authorizer
2775
2777
  resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
2776
2778
  api_id = module.http_api.api_id
@@ -3329,6 +3331,7 @@ resource "aws_lambda_function" "api_lambda" {
3329
3331
  mode = "Active"
3330
3332
  }
3331
3333
 
3334
+
3332
3335
  environment {
3333
3336
  variables = merge({
3334
3337
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -3392,6 +3395,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
3392
3395
  }
3393
3396
 
3394
3397
 
3398
+
3395
3399
  # Lambda integration for HTTP API
3396
3400
  resource "aws_apigatewayv2_integration" "lambda_integration" {
3397
3401
  api_id = module.http_api.api_id
@@ -3936,6 +3940,7 @@ resource "aws_lambda_function" "api_lambda" {
3936
3940
  mode = "Active"
3937
3941
  }
3938
3942
 
3943
+
3939
3944
  environment {
3940
3945
  variables = merge({
3941
3946
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -3999,6 +4004,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
3999
4004
  }
4000
4005
 
4001
4006
 
4007
+
4002
4008
  # Lambda integration for HTTP API
4003
4009
  resource "aws_apigatewayv2_integration" "lambda_integration" {
4004
4010
  api_id = module.http_api.api_id
@@ -4433,6 +4439,7 @@ resource "aws_lambda_function" "api_lambda" {
4433
4439
  mode = "Active"
4434
4440
  }
4435
4441
 
4442
+
4436
4443
  environment {
4437
4444
  variables = merge({
4438
4445
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -4495,6 +4502,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
4495
4502
  tags = var.tags
4496
4503
  }
4497
4504
 
4505
+
4498
4506
  # Cognito User Pool Authorizer
4499
4507
  resource "aws_api_gateway_authorizer" "cognito_authorizer" {
4500
4508
  name = "TestApiAuthorizer"
@@ -4665,6 +4673,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
4665
4673
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
4666
4674
  }
4667
4675
 
4676
+
4668
4677
  # Add API url to runtime config
4669
4678
  module "add_url_to_runtime_config" {
4670
4679
  source = "../../../core/runtime-config/entry"
@@ -5060,6 +5069,7 @@ resource "aws_lambda_function" "api_lambda" {
5060
5069
  mode = "Active"
5061
5070
  }
5062
5071
 
5072
+
5063
5073
  environment {
5064
5074
  variables = merge({
5065
5075
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -5123,6 +5133,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
5123
5133
  }
5124
5134
 
5125
5135
 
5136
+
5126
5137
  # Create proxy resource (captures all paths)
5127
5138
  resource "aws_api_gateway_resource" "proxy_resource" {
5128
5139
  rest_api_id = module.rest_api.api_id
@@ -5294,6 +5305,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
5294
5305
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
5295
5306
  }
5296
5307
 
5308
+
5297
5309
  # Add API url to runtime config
5298
5310
  module "add_url_to_runtime_config" {
5299
5311
  source = "../../../core/runtime-config/entry"
@@ -5689,6 +5701,7 @@ resource "aws_lambda_function" "api_lambda" {
5689
5701
  mode = "Active"
5690
5702
  }
5691
5703
 
5704
+
5692
5705
  environment {
5693
5706
  variables = merge({
5694
5707
  AWS_CONNECTION_REUSE_ENABLED = "1"
@@ -5752,6 +5765,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
5752
5765
  }
5753
5766
 
5754
5767
 
5768
+
5755
5769
  # Create proxy resource (captures all paths)
5756
5770
  resource "aws_api_gateway_resource" "proxy_resource" {
5757
5771
  rest_api_id = module.rest_api.api_id
@@ -5914,6 +5928,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
5914
5928
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
5915
5929
  }
5916
5930
 
5931
+
5917
5932
  # Add API url to runtime config
5918
5933
  module "add_url_to_runtime_config" {
5919
5934
  source = "../../../core/runtime-config/entry"
@@ -7,8 +7,12 @@ import {
7
7
  Function,
8
8
  FunctionProps,
9
9
  Tracing,
10
+ <%_ if (backend.type === 'fastapi') { _%>
11
+ LayerVersion,
12
+ SnapStartConf,
13
+ <%_ } _%>
10
14
  } from 'aws-cdk-lib/aws-lambda';
11
- import { Duration } from 'aws-cdk-lib';
15
+ import { Duration<%_ if (backend.type === 'fastapi') { _%>, Stack<%_ } _%> } from 'aws-cdk-lib';
12
16
  import {
13
17
  CorsHttpMethod,
14
18
  CfnApi,
@@ -109,7 +113,7 @@ export class <%= apiNameClassName %><
109
113
  ),
110
114
  <%_ } else if (backend.type === 'fastapi') { _%>
111
115
  runtime: Runtime.PYTHON_3_12,
112
- handler: '<%= backend.moduleName %>.main.handler',
116
+ handler: 'run.sh',
113
117
  code: Code.fromAsset(
114
118
  url.fileURLToPath(
115
119
  new URL(
@@ -121,15 +125,37 @@ export class <%= apiNameClassName %><
121
125
  <%_ } _%>
122
126
  timeout: Duration.seconds(30),
123
127
  tracing: Tracing.ACTIVE,
128
+ <%_ if (backend.type === 'fastapi') { _%>
129
+ snapStart: SnapStartConf.ON_PUBLISHED_VERSIONS,
130
+ <%_ } _%>
124
131
  environment: {
125
132
  AWS_CONNECTION_REUSE_ENABLED: '1',
133
+ <%_ if (backend.type === 'fastapi') { _%>
134
+ PORT: '8000',
135
+ AWS_LWA_INVOKE_MODE: 'buffered',
136
+ AWS_LAMBDA_EXEC_WRAPPER: '/opt/bootstrap',
137
+ <%_ } _%>
126
138
  },
127
139
  } satisfies FunctionProps,
128
140
  buildDefaultIntegration: (op, props: FunctionProps) => {
129
141
  const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
142
+ <%_ if (backend.type === 'fastapi') { _%>
143
+ const stack = Stack.of(scope);
144
+ handler.addLayers(
145
+ LayerVersion.fromLayerVersionArn(
146
+ scope,
147
+ `<%= apiNameClassName %>${op}LWALayer`,
148
+ `arn:aws:lambda:${stack.region}:753240598075:layer:LambdaAdapterLayerX86:24`,
149
+ ),
150
+ );
151
+ <%_ } _%>
130
152
  return {
131
153
  handler,
154
+ <%_ if (backend.type === 'fastapi') { _%>
155
+ integration: new HttpLambdaIntegration(`<%= apiNameClassName %>${op}Integration`, handler.currentVersion),
156
+ <%_ } else { _%>
132
157
  integration: new HttpLambdaIntegration(`<%= apiNameClassName %>${op}Integration`, handler),
158
+ <%_ } _%>
133
159
  };
134
160
  },
135
161
  });
@@ -7,19 +7,23 @@ import {
7
7
  Function,
8
8
  FunctionProps,
9
9
  Tracing,
10
+ <%_ if (backend.type === 'fastapi') { _%>
11
+ LayerVersion,
12
+ SnapStartConf,
13
+ <%_ } _%>
10
14
  } from 'aws-cdk-lib/aws-lambda';
11
15
  import {
12
16
  AuthorizationType,
13
17
  Cors,
14
18
  LambdaIntegration,
15
- <%_ if (backend.type === 'trpc') { _%>
19
+ <%_ if (['trpc', 'fastapi'].includes(backend.type)) { _%>
16
20
  ResponseTransferMode,
17
21
  <%_ } _%>
18
22
  <%_ if (auth === 'Cognito') { _%>
19
23
  CognitoUserPoolsAuthorizer,
20
24
  <%_ } _%>
21
25
  } from 'aws-cdk-lib/aws-apigateway';
22
- import { Duration } from 'aws-cdk-lib';
26
+ import { Duration<%_ if (backend.type === 'fastapi') { _%>, Stack<%_ } _%> } from 'aws-cdk-lib';
23
27
  import {
24
28
  PolicyDocument,
25
29
  PolicyStatement,
@@ -109,7 +113,7 @@ export class <%= apiNameClassName %><
109
113
  ),
110
114
  <%_ } else if (backend.type === 'fastapi') { _%>
111
115
  runtime: Runtime.PYTHON_3_12,
112
- handler: '<%= backend.moduleName %>.main.handler',
116
+ handler: 'run.sh',
113
117
  code: Code.fromAsset(
114
118
  url.fileURLToPath(
115
119
  new URL(
@@ -121,15 +125,37 @@ export class <%= apiNameClassName %><
121
125
  <%_ } _%>
122
126
  timeout: Duration.seconds(30),
123
127
  tracing: Tracing.ACTIVE,
128
+ <%_ if (backend.type === 'fastapi') { _%>
129
+ snapStart: SnapStartConf.ON_PUBLISHED_VERSIONS,
130
+ <%_ } _%>
124
131
  environment: {
125
132
  AWS_CONNECTION_REUSE_ENABLED: '1',
133
+ <%_ if (backend.type === 'fastapi') { _%>
134
+ PORT: '8000',
135
+ AWS_LWA_INVOKE_MODE: 'response_stream',
136
+ AWS_LAMBDA_EXEC_WRAPPER: '/opt/bootstrap',
137
+ <%_ } _%>
126
138
  },
127
139
  } satisfies FunctionProps,
128
140
  buildDefaultIntegration: (op, props: FunctionProps) => {
129
141
  const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
142
+ <%_ if (backend.type === 'fastapi') { _%>
143
+ const stack = Stack.of(scope);
144
+ handler.addLayers(
145
+ LayerVersion.fromLayerVersionArn(
146
+ scope,
147
+ `<%= apiNameClassName %>${op}LWALayer`,
148
+ `arn:aws:lambda:${stack.region}:753240598075:layer:LambdaAdapterLayerX86:24`,
149
+ ),
150
+ );
151
+ <%_ } _%>
130
152
  return {
131
153
  handler,
132
- <%_ if (backend.type === 'trpc') { _%>
154
+ <%_ if (backend.type === 'fastapi') { _%>
155
+ integration: new LambdaIntegration(handler.currentVersion, {
156
+ responseTransferMode: ResponseTransferMode.STREAM,
157
+ }),
158
+ <%_ } else if (backend.type === 'trpc') { _%>
133
159
  integration: new LambdaIntegration(handler, {
134
160
  responseTransferMode: ResponseTransferMode.STREAM,
135
161
  }),
@@ -132,11 +132,15 @@ resource "aws_lambda_function" "api_lambda" {
132
132
  handler = "index.handler"
133
133
  runtime = "nodejs22.x"
134
134
  <%_ } else if (backend.type === 'fastapi') { _%>
135
- handler = "<%= backend.moduleName %>.main.handler"
135
+ handler = "run.sh"
136
136
  runtime = "python3.12"
137
+ layers = ["arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24"]
137
138
  <%_ } _%>
138
139
  timeout = 30
139
140
  memory_size = 128
141
+ <%_ if (backend.type === 'fastapi') { _%>
142
+ publish = true
143
+ <%_ } _%>
140
144
 
141
145
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
142
146
 
@@ -145,9 +149,21 @@ resource "aws_lambda_function" "api_lambda" {
145
149
  mode = "Active"
146
150
  }
147
151
 
152
+ <%_ if (backend.type === 'fastapi') { _%>
153
+ # Enable SnapStart for faster cold starts
154
+ snap_start {
155
+ apply_on = "PublishedVersions"
156
+ }
157
+ <%_ } _%>
158
+
148
159
  environment {
149
160
  variables = merge({
150
161
  AWS_CONNECTION_REUSE_ENABLED = "1"
162
+ <%_ if (backend.type === 'fastapi') { _%>
163
+ PORT = "8000"
164
+ AWS_LWA_INVOKE_MODE = "buffered"
165
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
166
+ <%_ } _%>
151
167
  }, var.env)
152
168
  }
153
169
 
@@ -207,6 +223,17 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
207
223
  tags = var.tags
208
224
  }
209
225
 
226
+ <%_ if (backend.type === 'fastapi') { _%>
227
+ # Lambda alias pointing to the latest published version for SnapStart
228
+ resource "aws_lambda_alias" "live" {
229
+ name = "live"
230
+ function_name = aws_lambda_function.api_lambda.function_name
231
+ function_version = aws_lambda_function.api_lambda.version
232
+
233
+ depends_on = [aws_lambda_function.api_lambda]
234
+ }
235
+ <%_ } _%>
236
+
210
237
  <%_ if (auth === 'Cognito') { _%>
211
238
  # Cognito User Pool Authorizer
212
239
  resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
@@ -226,12 +253,20 @@ resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
226
253
  resource "aws_apigatewayv2_integration" "lambda_integration" {
227
254
  api_id = module.http_api.api_id
228
255
  integration_type = "AWS_PROXY"
256
+ <%_ if (backend.type === 'fastapi') { _%>
257
+ integration_uri = aws_lambda_alias.live.invoke_arn
258
+ <%_ } else { _%>
229
259
  integration_uri = aws_lambda_function.api_lambda.invoke_arn
260
+ <%_ } _%>
230
261
 
231
262
  payload_format_version = "2.0"
232
263
  timeout_milliseconds = 30000
233
264
 
265
+ <%_ if (backend.type === 'fastapi') { _%>
266
+ depends_on = [aws_lambda_alias.live]
267
+ <%_ } else { _%>
234
268
  depends_on = [aws_lambda_function.api_lambda]
269
+ <%_ } _%>
235
270
  }
236
271
 
237
272
  # Route for proxy integration (catches all requests)
@@ -268,15 +303,22 @@ module "add_url_to_runtime_config" {
268
303
  depends_on = [module.http_api]
269
304
  }
270
305
 
271
- # Lambda permission for API Gateway to invoke the function
306
+ # Lambda permission for API Gateway to invoke the function<% if (backend.type === 'fastapi') { %> via alias<% } %>
272
307
  resource "aws_lambda_permission" "api_gateway_invoke" {
273
308
  statement_id = "AllowExecutionFromAPIGateway"
274
309
  action = "lambda:InvokeFunction"
275
310
  function_name = aws_lambda_function.api_lambda.function_name
311
+ <%_ if (backend.type === 'fastapi') { _%>
312
+ qualifier = aws_lambda_alias.live.name
313
+ <%_ } _%>
276
314
  principal = "apigateway.amazonaws.com"
277
315
  source_arn = "${module.http_api.api_execution_arn}/*/*"
278
316
 
317
+ <%_ if (backend.type === 'fastapi') { _%>
318
+ depends_on = [module.http_api, aws_lambda_alias.live]
319
+ <%_ } else { _%>
279
320
  depends_on = [module.http_api, aws_lambda_function.api_lambda]
321
+ <%_ } _%>
280
322
  }
281
323
 
282
324
  # Outputs