@@ -960,6 +960,172 @@ When necessary, you can set a prefix when including a router object. This means
960
960
# many other related /users routing
961
961
```
962
962
963
+ #### Sample layout
964
+
965
+ !!! info "We use ALB to demonstrate that the UX remains the same"
966
+
967
+ This sample project contains an Users function with two distinct set of routes, ` /users ` and ` /health ` , and a single function to represent a fictitious ` users ` service.
968
+
969
+ The layout optimizes for code sharing, no custom build tooling, and it uses [ Lambda Layers] ( ../../index.md#lambda-layer ) to install Lambda Powertools.
970
+
971
+ === "Project layout"
972
+
973
+
974
+ ```python hl_lines="6 8 10-13"
975
+ .
976
+ ├── Pipfile # project app & dev dependencies; poetry, pipenv, etc.
977
+ ├── Pipfile.lock
978
+ ├── mypy.ini # namespace_packages = True
979
+ ├── .env # VSCode only. PYTHONPATH="users:${PYTHONPATH}"
980
+ ├── users
981
+ │ ├── requirements.txt # sam build detect it automatically due to CodeUri: users, e.g. pipenv lock -r > users/requirements.txt
982
+ │ ├── lambda_function.py # this will be our users Lambda fn; it could be split in folders if we want separate fns same code base
983
+ │ ├── constants.py
984
+ │ └── routers # routers module
985
+ │ ├── __init__.py
986
+ │ ├── users.py # /users routes, e.g. from routers import users; users.router
987
+ │ ├── health.py # /health routes, e.g. from routers import health; health.router
988
+ ├── template.yaml # SAM template.yml, CodeUri: users, Handler: users.main.lambda_handler
989
+ └── tests
990
+ ├── __init__.py
991
+ ├── unit
992
+ │ ├── __init__.py
993
+ │ └── test_users.py # unit tests for the users router
994
+ │ └── test_health.py # unit tests for the health router
995
+ └── functional
996
+ ├── __init__.py
997
+ ├── conftest.py # pytest fixtures for the functional tests
998
+ └── test_lambda_function.py # functional tests for the main lambda handler
999
+ ```
1000
+
1001
+ === "template.yml"
1002
+
1003
+ ```yaml hl_lines="20-21"
1004
+ AWSTemplateFormatVersion: '2010-09-09'
1005
+ Transform: AWS::Serverless-2016-10-31
1006
+ Description: Example service with multiple routes
1007
+ Globals:
1008
+ Function:
1009
+ Timeout: 10
1010
+ MemorySize: 512
1011
+ Runtime: python3.9
1012
+ Tracing: Active
1013
+ Environment:
1014
+ Variables:
1015
+ LOG_LEVEL: INFO
1016
+ POWERTOOLS_LOGGER_LOG_EVENT: true
1017
+ POWERTOOLS_METRICS_NAMESPACE: MyServerlessApplication
1018
+ POWERTOOLS_SERVICE_NAME: users
1019
+ Resources:
1020
+ UsersService:
1021
+ Type: AWS::Serverless::Function
1022
+ Properties:
1023
+ Handler: lambda_function.lambda_handler
1024
+ CodeUri: users
1025
+ Layers:
1026
+ # Latest version: https://awslabs.github.io/aws-lambda-powertools-python/latest/#lambda-layer
1027
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:3
1028
+ Events:
1029
+ ByUser:
1030
+ Type: Api
1031
+ Properties:
1032
+ Path: /users/{name}
1033
+ Method: GET
1034
+ AllUsers:
1035
+ Type: Api
1036
+ Properties:
1037
+ Path: /users
1038
+ Method: GET
1039
+ HealthCheck:
1040
+ Type: Api
1041
+ Properties:
1042
+ Path: /status
1043
+ Method: GET
1044
+ Outputs:
1045
+ UsersApiEndpoint:
1046
+ Description: "API Gateway endpoint URL for Prod environment for Users Function"
1047
+ Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod"
1048
+ AllUsersURL:
1049
+ Description: "URL to fetch all registered users"
1050
+ Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/users"
1051
+ ByUserURL:
1052
+ Description: "URL to retrieve details by user"
1053
+ Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/users/test"
1054
+ UsersServiceFunctionArn:
1055
+ Description: "Users Lambda Function ARN"
1056
+ Value: !GetAtt UsersService.Arn
1057
+ ```
1058
+
1059
+ === "users/lambda_function.py"
1060
+
1061
+ ```python hl_lines="9 15-16"
1062
+ from typing import Dict
1063
+
1064
+ from aws_lambda_powertools import Logger, Tracer
1065
+ from aws_lambda_powertools.event_handler import ApiGatewayResolver
1066
+ from aws_lambda_powertools.event_handler.api_gateway import ProxyEventType
1067
+ from aws_lambda_powertools.logging.correlation_paths import APPLICATION_LOAD_BALANCER
1068
+ from aws_lambda_powertools.utilities.typing import LambdaContext
1069
+
1070
+ from routers import health, users
1071
+
1072
+ tracer = Tracer()
1073
+ logger = Logger()
1074
+ app = ApiGatewayResolver(proxy_type=ProxyEventType.ALBEvent)
1075
+
1076
+ app.include_router(health.router)
1077
+ app.include_router(users.router)
1078
+
1079
+
1080
+ @logger.inject_lambda_context(correlation_id_path=APPLICATION_LOAD_BALANCER)
1081
+ @tracer.capture_lambda_handler
1082
+ def lambda_handler(event: Dict, context: LambdaContext):
1083
+ return app.resolve(event, context)
1084
+ ```
1085
+
1086
+ === "users/routers/health.py"
1087
+
1088
+ ```python hl_lines="4 6-7 10"
1089
+ from typing import Dict
1090
+
1091
+ from aws_lambda_powertools import Logger
1092
+ from aws_lambda_powertools.event_handler.api_gateway import Router
1093
+
1094
+ router = Router()
1095
+ logger = Logger(child=True)
1096
+
1097
+
1098
+ @router.get("/status")
1099
+ def health() -> Dict:
1100
+ logger.debug("Health check called")
1101
+ return {"status": "OK"}
1102
+ ```
1103
+
1104
+ === "tests/functional/test_users.py"
1105
+
1106
+ ```python hl_lines="3"
1107
+ import json
1108
+
1109
+ from users import main # follows namespace package from root
1110
+
1111
+
1112
+ def test_lambda_handler(apigw_event, lambda_context):
1113
+ ret = main.lambda_handler(apigw_event, lambda_context)
1114
+ expected = json.dumps({"message": "hello universe"}, separators=(",", ":"))
1115
+
1116
+ assert ret["statusCode"] == 200
1117
+ assert ret["body"] == expected
1118
+ ```
1119
+
1120
+ === ".env"
1121
+
1122
+ > Note: It is not needed for PyCharm (select folder as source).
1123
+
1124
+ This is necessary for Visual Studio Code, so integrated tooling works without failing import.
1125
+
1126
+ ```bash
1127
+ PYTHONPATH="users:${PYTHONPATH}"
1128
+ ```
963
1129
964
1130
#### Trade-offs
965
1131
0 commit comments