You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/quickstart.md
+60-33Lines changed: 60 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,11 +142,15 @@ At the end of the deployment, you will find the API endpoint URL within `Outputs
142
142
!!! Info
143
143
For more details on AWS SAM deployment mechanism, see [SAM Deploy reference docs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html).
144
144
145
-
## API Gateway router
146
-
Let's expand our application with a new method. It takes an username as a input and return it in the response.
145
+
## Routing
147
146
148
-
We decided to write another Lambda including required method. Next, we configure our API Gateway to expose this Lambda under a new unique path `/hello/{name}`.
149
-
=== "app_name.py"
147
+
### Adding a new route
148
+
149
+
Let's expand our application with a new route - `/hello/{name}`. It will accept an username as a path input and return it in the response.
150
+
151
+
For this to work, we could create a new Lambda function to handle incoming requests for `/hello/{name}` - It'd look like this:
152
+
153
+
=== "hello_by_name.py"
150
154
151
155
```python
152
156
import json
@@ -163,7 +167,7 @@ We decided to write another Lambda including required method. Next, we configure
163
167
164
168
=== "template.yaml"
165
169
166
-
```yaml hl_lines="20-31"
170
+
```yaml hl_lines="21-32"
167
171
AWSTemplateFormatVersion: "2010-09-09"
168
172
Transform: AWS::Serverless-2016-10-31
169
173
Description: Sample SAM Template for powertools-quickstart
@@ -183,11 +187,12 @@ We decided to write another Lambda including required method. Next, we configure
183
187
Properties:
184
188
Path: /hello
185
189
Method: get
186
-
HelloWorldFunctionName:
190
+
191
+
HelloWorldByNameFunctionName:
187
192
Type: AWS::Serverless::Function
188
193
Properties:
189
194
CodeUri: hello_world/
190
-
Handler: app_name.lambda_handler
195
+
Handler: hello_by_name.lambda_handler
191
196
Runtime: python3.9
192
197
Events:
193
198
HelloWorldName:
@@ -201,20 +206,26 @@ We decided to write another Lambda including required method. Next, we configure
This way certainly works for simple use case. But what happens if your application gets bigger and we need to cover numerous URL paths and HTTP methods for them? If that is the case, we should:
209
+
???+ question
210
+
But what happens if your application gets bigger and we need to cover numerous URL paths and HTTP methods for them?
205
211
206
-
* Add a new Lambda handler with business logic for each new URL path and HTTP method used.
207
-
* Add a new Lambda configuration to a SAM template file to map the Lambda function to the required path and HTTP URL method.
212
+
**This would quickly become non-trivial to maintain**. Adding new Lambda function for each path, or multiple if/else to handle several routes & HTTP Methods can be error prone.
208
213
209
-
This could result in a number of alike Lambda files and large SAM configuration file with similar configuration sections.
210
-
if we see that the addition of new URL paths lead to the boilerplate code, we should lean towards the routing approach.
211
-
!!! Info
212
-
If you want a more detailed explanation of these two approaches, we have explained the considerations [here](.. /core/event_handler/api_gateway/#considerations)
214
+
### Creating our own router
215
+
216
+
???+ question
217
+
What if we create a simple router to reduce boilerplate?
218
+
219
+
We could group similar routes and intents, separate read and write operations resulting in fewer functions. It doesn't address the boilerplate routing code, but maybe it will be easier to add additional URLs.
220
+
221
+
!!! Info "Info: You might be already asking yourself about mono vs micro-functions"
222
+
If you want a more detailed explanation of these two approaches, head over to the [trade-offs on each approach](../core/event_handler/api_gateway/#considerations){target="_blank"} later.
223
+
224
+
A first attempt at the routing logic might look similar to the following code snippet.
213
225
214
-
The simple code might look similar to the following code snippet.
215
226
=== "app.py"
216
227
217
-
```python hl_lines="4 9 13 29-31 37-38"
228
+
```python hl_lines="4 9 13 27-29 35-36"
218
229
import json
219
230
220
231
@@ -237,12 +248,10 @@ The simple code might look similar to the following code snippet.
237
248
def get(self, path, method):
238
249
try:
239
250
route = self.routes[f"{path}-{method}"]
240
-
except:
241
-
print("Cannot route request to correct method")
242
-
raise NotImplemented
251
+
except KeyError:
252
+
raise RuntimeError(f"Cannot route request to correct method. path={path}, method={method}")
* We add two methods: `hello_name` and `hello` (line 4,9).
292
-
* We add the `Router` class which allows us to record the method that should be called when the specific request arrives (line 13).
293
-
* We create the instance and added the configuration with the mapping of the processing methods and the http query method (line 29-31).
294
-
* In the Lambda handler, we call router instance `get` method to retrieve a reference to the processing method (`hello` or `hello_name`).(line 37).
295
-
* Finally, we run this method and send the results back to API Gateway (line 38).
300
+
Let's break this down:
301
+
302
+
***L4-9**: We defined two `hello_name` and `hello` functions to handle `/hello/{name}` and `/hello` routes
303
+
***L13:** We added a `Router` class to map a path, a method, and the function to call
304
+
***L27-29**: We create a `Router` instance and map both `/hello` and `/hello/{name}`
305
+
***L35:** We use Router's `get` method to retrieve a reference to the processing method (`hello` or `hello_name`)
306
+
***L36:** Finally, we run this method and send the results back to API Gateway
296
307
297
-
This approach simplifies the configuration of our infrastructure since we have added all API Gateway paths in the `HelloWorldFunction` event section. We need to understand the internal structure of the API Gateway request events, to deduce the requested path, http method and path parameters. This puts additional engineering effort to provide proper error handling. Also, if we decide to use another event source for our Lambda, since we are highly coupled it requires rewriting of our Lambda handler to get the information we need.
308
+
This approach simplifies the configuration of our infrastructure since we have added all API Gateway paths in the `HelloWorldFunction` event section.
298
309
299
-
Let's see how we can improve it with Powertools.
310
+
However, it forces us to understand the internal structure of the API Gateway request events, responses, and it could lead to other errors such as CORS not being handled properly, error handling, etc.
311
+
312
+
### Simplifying with Event Handler
313
+
314
+
We can massively simplify cross-cutting concerns while keeping it lightweight by using [Event Handler](./core/event_handler/api_gateway.md){target="_blank"}
315
+
316
+
!!! tip
317
+
This is available for both [REST API (API Gateway, ALB)](./core/event_handler/api_gateway.md){target="_blank"} and [GraphQL API (AppSync)](./core/event_handler/appsync.md){target="_blank"}.
318
+
319
+
Let's include Lambda Powertools as a dependency in `requirement.txt`, and use Event Handler to refactor our previous example.
300
320
301
321
=== "app.py"
302
322
@@ -310,12 +330,12 @@ Let's see how we can improve it with Powertools.
@@ -327,10 +347,17 @@ Let's see how we can improve it with Powertools.
327
347
aws-lambda-powertools
328
348
```
329
349
330
-
Powertools provides an `ApiGatewayResolver` class, which helps understand the structure, no need to look it up.
350
+
Use `sam build && sam local start-api` and try run it locally again.
351
+
352
+
???+ note
353
+
If you're coming from [Flask](https://flask.palletsprojects.com/en/2.0.x/){target="_blank"}, you will be familiar with this experience already. [Event Handler for API Gateway](./core/event_handler/api_gateway.md){target="_blank"} uses `ApiGatewayResolver` to give a Flask-like experience while staying true to our tenet `Keep it lean`.
354
+
355
+
We have added the route annotation as the decorator for our methods. It enables us to use the parameters passed in the request directly, and our responses are simply dictionaries.
356
+
357
+
Lastly, we used `return app.resolve(event, context)` so Event Handler can resolve routes, inject the current request, handle serialization, route validation, etc.
358
+
359
+
From here, we could handle [404 routes](./core/event_handler/api_gateway.md#handling-not-found-routes){target="_blank"}, [error handling](./core/event_handler/api_gateway.md#http://127.0.0.1:8000/core/event_handler/api_gateway/#exception-handling){target="_blank"}, [access query strings, payload, etc.](./core/event_handler/api_gateway.md#http://127.0.0.1:8000/core/event_handler/api_gateway#accessing-request-details){target="_blank"}.
331
360
332
-
We have added the route annotation as the decorator for our methods. It enables us to use the parameters passed in the request directly.
333
-
We have also specified Lambda Powertools package in our `requirement.txt` file to ensure SAM is able to build our Lambda.
334
361
335
362
!!! tip
336
363
If you'd like to learn how python decorators work under the hood, you can follow [Real Python](https://realpython.com/primer-on-python-decorators/)'s article.
0 commit comments