Skip to content

Commit 6584364

Browse files
committed
docs(quickstart): add sub-sections, fix highlight & code
1 parent 0951f8d commit 6584364

File tree

1 file changed

+60
-33
lines changed

1 file changed

+60
-33
lines changed

docs/quickstart.md

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,15 @@ At the end of the deployment, you will find the API endpoint URL within `Outputs
142142
!!! Info
143143
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).
144144

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
147146

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"
150154

151155
```python
152156
import json
@@ -163,7 +167,7 @@ We decided to write another Lambda including required method. Next, we configure
163167

164168
=== "template.yaml"
165169

166-
```yaml hl_lines="20-31"
170+
```yaml hl_lines="21-32"
167171
AWSTemplateFormatVersion: "2010-09-09"
168172
Transform: AWS::Serverless-2016-10-31
169173
Description: Sample SAM Template for powertools-quickstart
@@ -183,11 +187,12 @@ We decided to write another Lambda including required method. Next, we configure
183187
Properties:
184188
Path: /hello
185189
Method: get
186-
HelloWorldFunctionName:
190+
191+
HelloWorldByNameFunctionName:
187192
Type: AWS::Serverless::Function
188193
Properties:
189194
CodeUri: hello_world/
190-
Handler: app_name.lambda_handler
195+
Handler: hello_by_name.lambda_handler
191196
Runtime: python3.9
192197
Events:
193198
HelloWorldName:
@@ -201,20 +206,26 @@ We decided to write another Lambda including required method. Next, we configure
201206
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
202207
```
203208

204-
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?
205211

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.
208213

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.
213225

214-
The simple code might look similar to the following code snippet.
215226
=== "app.py"
216227

217-
```python hl_lines="4 9 13 29-31 37-38"
228+
```python hl_lines="4 9 13 27-29 35-36"
218229
import json
219230

220231

@@ -237,12 +248,10 @@ The simple code might look similar to the following code snippet.
237248
def get(self, path, method):
238249
try:
239250
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}")
243253
return route
244254

245-
246255
router = Router()
247256
router.set(path="/hello", method="GET", handler=hello)
248257
router.set(path="/hello/{name}", method="GET", handler=hello_name)
@@ -257,7 +266,7 @@ The simple code might look similar to the following code snippet.
257266

258267
=== "template.yaml"
259268

260-
```yaml hl_lines="15-25"
269+
```yaml hl_lines="15-24"
261270
AWSTemplateFormatVersion: "2010-09-09"
262271
Transform: AWS::Serverless-2016-10-31
263272
Description: Sample SAM Template for powertools-quickstart
@@ -288,15 +297,26 @@ The simple code might look similar to the following code snippet.
288297
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
289298
```
290299

291-
* 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
296307

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.
298309

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.
300320

301321
=== "app.py"
302322

@@ -310,12 +330,12 @@ Let's see how we can improve it with Powertools.
310330

311331
@app.get("/hello/<name>")
312332
def hello_name(name):
313-
return {"statusCode": 200, "body": json.dumps({"message": f"hello {name}!"})}
333+
return {"message": f"hello {name}!"}
314334

315335

316336
@app.get("/hello")
317337
def hello():
318-
return {"statusCode": 200, "body": json.dumps({"message": "hello unknown!"})}
338+
return {"message": "hello unknown!"}
319339

320340

321341
def lambda_handler(event, context):
@@ -327,10 +347,17 @@ Let's see how we can improve it with Powertools.
327347
aws-lambda-powertools
328348
```
329349

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"}.
331360

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.
334361

335362
!!! tip
336363
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

Comments
 (0)