Amazon API Gateway + AWS Lambda is a powerful duo for creating easy-to-deploy scalable infrastructure that is immediately accessible by thousands of users. For this post, we will create an API Gateway with an lambda proxy integration using the AWS Lambda Powertools library and the AWS CDK.
In just a few lines of code, we’ll have our API deployed and ready to go! This tutorial assumes you know what the Amazon API Gateway and AWS Lambda services are.
AWS Lambda Powertools is an SDK released by Amazon that aims to increase developer productiviy by bundling similar functionality together:
and more!
With this SDK, we’ll build a single lambda integration that will handle all the routes of our API Gateway.
If you’ve used Flask, Spring Boot, express, etc., this following code snippet should look familiar:
1from aws_lambda_powertools import Logger, Tracer
2from aws_lambda_powertools.event_handler import APIGatewayRestResolver
3from aws_lambda_powertools.logging import correlation_paths
4from aws_lambda_powertools.utilities.typing import LambdaContext
5from aws_lambda_powertools.utilities.data_classes import event_source
6from aws_lambda_powertools.utilities.data_classes.api_gateway_authorizer_event import (
7 APIGatewayAuthorizerRequestEvent,
8)
9
10tracer = Tracer()
11logger = Logger()
12app = APIGatewayRestResolver()
13
14
15@app.get("/pets")
16@tracer.capture_method
17def get_pets():
18 return {"pets": []}
19
20
21@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
22@tracer.capture_lambda_handler
23@event_source(data_class=APIGatewayAuthorizerRequestEvent)
24def lambda_handler(
25 event: APIGatewayAuthorizerRequestEvent, context: LambdaContext
26) -> dict:
27 return app.resolve(event.raw_event, context)
The APIGatewayRestResolver
behaves like a router. The lambda_handler
processes the API Gateway event then resolve
s the route using the information that was passed from the lambda context. We’ll reference this example when creating our own.
Follow the instructions below to deploy a Lambda Function with Powertools bundled for an API Gateway proxy integration.
Create a requirements.txtFor this tutorial, version 2.118.0 of the AWS CDK was used.
1aws-cdk-lib==2.118.0
2constructs>=10.0.0,<11.0.0
Install the dependencies
1pip3 install -r requirements.txt
Create /handler folder with an index.py file
1import json
2
3
4def handler(event, context):
5 return {"statusCode": 200, "body": json.dumps({"message": "Hello world!"})}
Create stack.py with an API Gateway and a lambda router
1from os import path
2
3import aws_cdk as cdk
4import aws_cdk.aws_lambda as lambda_
5import aws_cdk.aws_apigateway as apigateway
6
7
8class ApiStack(cdk.Stack):
9 def __init__(self, scope: cdk.App, construct_id: str, **kwargs) -> None:
10 super().__init__(scope, construct_id, **kwargs)
11
12 lambda_function = lambda_.Function(
13 self,
14 "Function",
15 runtime=lambda_.Runtime.PYTHON_3_11,
16 handler="index.handler",
17 code=lambda_.Code.from_asset(path.join(path.dirname(__file__), "handler")),
18 )
19
20 api = apigateway.LambdaRestApi(self, "API", handler=lambda_function)
The AWS CDK provides “L3” constructs; constructs that abstract away configuration we otherwise explicitly need define. In the case above, the L3 construct named LambdaRestApi
handles a few things:
{proxy+}
creationANY
method creation1import aws_cdk as cdk
2
3from stack import ApiStack
4
5app = cdk.App()
6ApiStack(app, "ApiStack")
7
8app.synth()
Create cdk.json
1{
2 "app": "python3 app.py"
3}
The directory structure should look like this:
1project/
2âââ app.py
3âââ cdk.json
4âââ handler
5â  âââ index.py
6âââ requirements.txt
7âââ stack.py
Deploy the stack
After the stack is deployed, we’re given the API endpoint in a stack output:
1Outputs:
2ApiStack.APIEndpoint1793E782 = https://{api_id}.execute-api.us-east-1.amazonaws.com/prod/
Testing the proxy integration
At this point, we can send any HTTP request and always get back the same response:
1curl https://{api_id}.execute-api.us-east-1.amazonaws.com/prod/
Output:
1{"message": "Hello world!"}
Add Lambda Powertools lambda layer using AWS CDK + SAR
Rather than write our own routing function based on the API Gateway event, let’s use AWS Lambda Powertools.
I have two preferred options for adding the Lambda Powertools layer:
For the purposes of this tutorial, I’ll reference the lambda layer using the serverless application respository.
1from os import path
2
3import aws_cdk as cdk
4import aws_cdk.aws_lambda as lambda_
5import aws_cdk.aws_apigateway as apigateway
6import aws_cdk.aws_sam as sam
7
8POWERTOOLS_BASE_NAME = "AWSLambdaPowertools"
9POWERTOOLS_VERSION = "2.30.2"
10POWERTOOLS_ARN = "arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer"
11
12
13class ApiStack(cdk.Stack):
14 def __init__(self, scope: cdk.App, construct_id: str, **kwargs) -> None:
15 super().__init__(scope, construct_id, **kwargs)
16
17 powertools_app = sam.CfnApplication(
18 self,
19 f"{POWERTOOLS_BASE_NAME}Application",
20 location={
21 "applicationId": POWERTOOLS_ARN,
22 "semanticVersion": POWERTOOLS_VERSION,
23 },
24 )
25
26 powertools_layer_arn = powertools_app.get_att(
27 "Outputs.LayerVersionArn"
28 ).to_string()
29
30 powertools_layer_version = lambda_.LayerVersion.from_layer_version_arn(
31 self, f"{POWERTOOLS_BASE_NAME}", powertools_layer_arn
32 )
33
34 lambda_function = lambda_.Function(
35 self,
36 "Function",
37 runtime=lambda_.Runtime.PYTHON_3_11,
38 handler="index.handler",
39 code=lambda_.Code.from_asset(path.join(path.dirname(__file__), "handler")),
40 layers=[powertools_layer_version],
41 )
42
43 api = apigateway.LambdaRestApi(self, "API", handler=lambda_function)
Using AWS Lambda Powertools
Now that the lambda is successfully configured for AWS Lambda Powertools, let’s experiment using a small todo app:
1from aws_lambda_powertools import Logger, Tracer
2from aws_lambda_powertools.event_handler import APIGatewayRestResolver
3from aws_lambda_powertools.logging import correlation_paths
4from aws_lambda_powertools.utilities.typing import LambdaContext
5
6tracer = Tracer()
7logger = Logger()
8app = APIGatewayRestResolver()
9
10
11@app.get("/tasks")
12@tracer.capture_method
13def get_tasks():
14 # database lookup goes here
15 return [{"id": "ptvWZ3", "text": "hello!"}, {"id": "cqDUr3", "text": "another!"}]
16
17
18@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
19@tracer.capture_lambda_handler
20def handler(event: dict, context: LambdaContext) -> dict:
21 return app.resolve(event, context)
Now we can test the different routes:
1curl https://{api_id}.execute-api.us-east-1.amazonaws.com/prod/
Output:
1{"statusCode":404,"message":"Not found"}
and the real /tasks
endpoint:
1curl https://{api_id}.execute-api.us-east-1.amazonaws.com/prod/tasks
Output:
1[{"id":"ptvWZ3","text":"hello!"},{"id":"cqDUr3","text":"another!"}]
Conclusion
At this point, you will have a functioning AWS CDK application with an API Gateway Lambda Proxy integration setup! While the /tasks
code is rudimentary, it provides the foundation for expansion for your own API!
Happy building!
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4