how.wtf

Serverless FastAPI with AWS Lambda, API Gateway, and AWS CDK

· Thomas Taylor

API Gateway, Lambda, and FastAPI logos in a simple architecture image

By the end of this article, you’ll have a Python app deployed with FastAPI using AWS Lambda and API Gateway that is serving requests. In addition, you will have the infrastructure defined in code.

What is AWS Lambda and API Gateway

AWS Lambda and Amazon API Gateway are two services offered by AWS that enable developers to quickly spin up infrastructure without the hassle of provisioning or maintaining servers. In our case, we’ll host the FastAPI app in a Lambda function and the API Gateway will route requests to it.

This approach is immediately scalable upon deployment and includes a generous free tier by AWS.

How to deploy FastAPI with AWS Lambda + API Gateway + AWS CDK

Follow the instructions below to successfully deploy an AWS CDK app that includes a lambda function and API Gateway.

Create a requirements.txt

Create a requirements.txt file with the following information:

1aws-cdk-lib==2.118.0
2aws-cdk.aws-lambda-python-alpha==2.118.0a0
3constructs>=10.0.0,<11.0.0
4fastapi==0.108.0
5uvicorn==0.25.0
6mangum==0.17.0

To serve FastAPI locally, I chose uvicorn. This is not mandatory if you do not care about testing locally. To adapt the FastAPI router for API Gateway, we will use mangum.

Install the dependencies

1pip3 install -r requirements.txt

Create /app folder with a main.py file

You can use your preferred file structure. Whether you use this:

 1project/
 2├── app
 3│   ├── __init__.py
 4│   ├── main.py
 5│   ├── dependencies.py
 6│   └── routers
 7│   │   ├── __init__.py
 8│   │   ├── items.py
 9│   │   └── users.py
10│   └── internal
11│       ├── __init__.py
12│       └── admin.py

or this

1project/
2├── app
3│   ├── __init__.py
4│   └── main.py

ensure that there’s a main.py at the root of the app folder.

For the purposes of this tutorial, we’ll use a app/main.py file to keep it simple for a small todo app:

 1from fastapi import FastAPI
 2from mangum import Mangum
 3
 4app = FastAPI()
 5
 6
 7@app.get("/tasks")
 8async def get_tasks():
 9    # database lookup goes here
10    return [{"id": "ptvWZ3", "text": "hello!"}, {"id": "cqDUr3", "text": "another!"}]
11
12
13@app.get("/")
14async def root():
15    return {"message": "Hello World!"}
16
17
18handler = Mangum(app, lifespan="off")

Create a 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
 6import aws_cdk.aws_lambda_python_alpha as python
 7
 8
 9class ApiStack(cdk.Stack):
10    def __init__(self, scope: cdk.App, construct_id: str, **kwargs) -> None:
11        super().__init__(scope, construct_id, **kwargs)
12
13        lambda_function = python.PythonFunction(
14            self,
15            "Function",
16            entry=path.join(path.dirname(__file__), "app"),
17            runtime=lambda_.Runtime.PYTHON_3_11,
18            index="main.py",
19        )
20
21        api = apigateway.LambdaRestApi(self, "API", handler=lambda_function)

NOTE: We are using a construct that is in alpha. This enables the AWS CDK to use Docker to bundle our Python requirements together.

This seems small, but we are leveraging the power of the AWS CDK and their L3 constructs. This CDK app will:

  1. Bundle python requirements and create a .zip file
  2. Create a lambda function with the handler as main.handler
  3. Create an API Gateway
  4. Add a resource path of {proxy+} to the API
  5. Add an ANY method to the resource path that points to the lambda function
  6. Enable a “proxy integration” for the lambda function

What is an API Gateway proxy integration?

For the FastAPI logic to work appropriately, we need to configure the API Gateway to forward full API requests to the lambda function. With this setting, the lambda function’s responsibility increases as it must fulfill the requirements of the API Gateway service.

Fortunately, mangum takes care of this.

Create a requirements.txt file for the /app folder

As of time of writing (Jan. 7th, 2024), the python alpha function entry folder must have the requirements.txt file in it. Create app/requirements.txt file with the following:

1fastapi==0.108.0
2mangum==0.17.0

Create infra.py

Create a file that provisions the CDK app. I named mine infra.py.

1import aws_cdk as cdk
2
3from stack import ApiStack
4
5app = cdk.App()
6ApiStack(app, "ApiStack")
7
8app.synth()

Create cdk.json

Ensure that the entry file matches the one you created before.

1{
2  "app": "python3 infra.py"
3}

The directory structure should look like this:

1project/
2├── app
3│   ├── main.py
4│   └── requirements.txt
5├── cdk.json
6├── infra.py
7├── requirements.txt
8└── stack.py

Deploy the stack

Before deploying, ensure that Docker is running.

1cdk deploy

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

We’ll use curl to test our API:

1curl https://{api_id}.execute-api.us-east-1.amazonaws.com/prod/

Output:

1{"message": "Hello world!"}

The /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!"}]

Testing locally

The top-level requirements.txt enables us to test locally with uvicorn:

1uvicorn app.main:app --reload

Using curl,

1curl http://127.0.0.1:8000/

Output:

1{"message":"Hello World!"}

Cleaning up

If you wish to destroy your CDK app, run the following command:

1cdk destroy

Conclusion

After following the prior steps, you will have an understanding of how to deploy a functioning API Gateway and Lambda function powered by FastAPI.

Enjoy building on this example!

#python   #aws   #aws-cdk   #serverless  

Reply to this post by email ↪