Skip to content

Commit 3632d18

Browse files
committed
docs: break down into smaller sections
1 parent 944dc0c commit 3632d18

File tree

1 file changed

+117
-11
lines changed

1 file changed

+117
-11
lines changed

docs/core/event_handler/api_gateway.md

+117-11
Original file line numberDiff line numberDiff line change
@@ -853,27 +853,129 @@ You can instruct API Gateway handler to use a custom serializer to best suit you
853853
}
854854
```
855855

856-
### Splitting routes across multiple files
856+
### Split routes with Router
857857

858-
When building a larger application, sometimes to helps to split out your routes into multiple file. Also
859-
there might be cases where you have some shared routes for multiple lambdas like a `health` status lambda
860-
to be used with Application Load Balancer.
858+
As you grow the number of routes a given Lambda function should handle, it is natural to split routes into separate files to ease maintenance - That's where the `Router` feature is useful.
861859

862-
Below is an example project layout for AWS Lambda Functions using AWS SAM CLI that allows for relative path
863-
imports (ie: `from .routers import health`).
860+
Let's assume you have `app.py` as your Lambda function entrypoint and routes in `users.py`, this is how you'd use the `Router` feature.
864861

865-
!!! tip "See in `src/app/main.py`, when including a route we can add a prefix to those routes ie: `prefix="/health"`."
866-
!!! tip "See in `src/app/routers/health.py`, when adding a child logger we use `Logger(child=True)`."
862+
=== "users.py"
863+
864+
We import **Router** instead of **ApiGatewayResolver**; syntax wise is exactly the same.
865+
866+
```python hl_lines="4 8 12 15 21"
867+
import itertools
868+
from typing import Dict
869+
870+
from aws_lambda_powertools import Logger
871+
from aws_lambda_powertools.event_handler.api_gateway import Router
872+
873+
logger = Logger(child=True)
874+
router = Router()
875+
876+
USERS = {"user1": "details_here", "user2": "details_here", "user3": "details_here"}
877+
878+
@router.get("/users")
879+
def get_users() -> Dict:
880+
# get query string ?limit=10
881+
pagination_limit = router.current_event.get_query_string_value(name="limit", default_value=10)
882+
883+
logger.info(f"Fetching the first {pagination_limit} users...")
884+
ret = dict(itertools.islice(USERS.items(), pagination_limit))
885+
return {"items": [ret]}
886+
887+
@router.get("/users/<username>")
888+
def get_user(username: str) -> Dict:
889+
logger.info(f"Fetching username {username}")
890+
return {"details": USERS.get(username, {})}
891+
892+
# many other related /users routing
893+
```
894+
895+
=== "app.py"
896+
897+
We use `include_router` method and include all user routers registered in the `router` global object.
898+
899+
```python hl_lines="6 8-9"
900+
from typing import Dict
901+
902+
from aws_lambda_powertools.event_handler import ApiGatewayResolver
903+
from aws_lambda_powertools.utilities.typing import LambdaContext
904+
905+
import users
906+
907+
app = ApiGatewayResolver()
908+
app.include_router(users.router)
909+
910+
911+
def lambda_handler(event: Dict, context: LambdaContext):
912+
app.resolve(event, context)
913+
```
914+
915+
#### Route prefix
916+
917+
As routes are now split in their own files, you can optionally instruct `Router` to inject a prefix for all routes during registration.
918+
919+
In the previous example, `users.py` routes had a `/users` prefix. We could remove `/users` from all route definitions, and then set `include_router(users.router, prefix="/users")` in the `app.py`.
920+
921+
=== "app.py"
922+
923+
```python hl_lines="9"
924+
from typing import Dict
925+
926+
from aws_lambda_powertools.event_handler import ApiGatewayResolver
927+
from aws_lambda_powertools.utilities.typing import LambdaContext
928+
929+
import users
930+
931+
app = ApiGatewayResolver()
932+
app.include_router(users.router, prefix="/users") # prefix '/users' to any route in `users.router`
933+
934+
def lambda_handler(event: Dict, context: LambdaContext):
935+
app.resolve(event, context)
936+
```
937+
938+
=== "users.py"
939+
940+
```python hl_lines="11 15"
941+
from typing import Dict
942+
943+
from aws_lambda_powertools import Logger
944+
from aws_lambda_powertools.event_handler.api_gateway import Router
945+
946+
logger = Logger(child=True)
947+
router = Router()
948+
949+
USERS = {"user1": "details_here", "user2": "details_here", "user3": "details_here"}
950+
951+
@router.get("/") # /users, when we set the prefix in app.py
952+
def get_users() -> Dict:
953+
...
954+
955+
@router.get("/<username>")
956+
def get_user(username: str) -> Dict:
957+
...
958+
959+
# many other related /users routing
960+
```
961+
962+
#### Sample larger layout
963+
964+
Below is an example project layout where we have Users routes similar to the previous example, and health check route - We use ALB to demonstrate the UX remains the same.
965+
966+
Note that this layout optimizes for code sharing and for those familiar with Python modules. This means multiple functions will share the same `CodeUri` and package, though they are only built once.
967+
968+
!!! tip "External dependencies can be [built as a Lambda Layer](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-layers.html){target="_blank"} and set as `dev` dependencies for the project, though outside of scope for this documentation."
867969

868970
=== "Project layout"
869971

870-
```text
972+
```python hl_lines="10-13"
871973
.
872-
├── Pipfile
974+
├── Pipfile # project dev dependencies
873975
├── Pipfile.lock
874976
├── src
875977
│ ├── __init__.py
876-
│ ├── requirements.txt # pipenv lock -r > src/requirements.txt
978+
│ ├── requirements.txt # dummy for `sam build`, as external deps are Lambda Layers
877979
│ └── app
878980
│ ├── __init__.py # this file makes "app" a "Python package"
879981
│ ├── main.py # Main lambda handler (app.py, index.py, handler.py)
@@ -1005,6 +1107,10 @@ imports (ie: `from .routers import health`).
10051107
assert ret["body"] == expected
10061108
```
10071109

1110+
#### Trade-offs
1111+
1112+
!!! todo "TODO"
1113+
10081114
## Testing your code
10091115

10101116
You can test your routes by passing a proxy event request where `path` and `httpMethod`.

0 commit comments

Comments
 (0)