How to deploy your FastAPI application on AWS Lambda with Serverless

April 10th, 2021

Adem Usta

FastAPI AWS Lambda Serverless

Introduction

FastAPI, the python package created by Sebastián Ramírez, is a great framework that allows you to create REST APIs.

It's quite straightforward to take the control of it.

If you didn't know this package, I wisely invite you to visit the official documentation and create your APIs with it !

If you came accross this article, you are probably looking for a way to deploy your FastAPI application on AWS Lambda, the Amazon serverless ecosystem.

Good pick ! We will try to explain to you how to proceed.

Tutorial

Step 1 - Create a simple FastAPI app

By following the official guide, we can code an application with a few lines of code.

Let's create a file main.py that declares the app and a /hello route:

from fastapi import FastAPI app = FastAPI(title="MyAwesomeApp") @app.get("/hello") def hello_world(): return {"message": "Hello World"}

Step 2 - Test your application locally

To test the app locally, let's create a virtual environment and install what's inside the requirements.txt file:

fastapi==0.63.0 uvicorn==0.13.4

Some commands to do that:

python3.8 -m venv .virtualenv # Creates a virtual environment source .virtualenv/bin/activate # Activates it in the current shell pip install -r requirements.txt # Installs python packages

Deploy your application locally:

uvicorn main:app

You should see some logs like these:

INFO: Started server process [87999] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Your app is available here: http://127.0.0.1:8000.

You can check the autogenerated documentation here: http://127.0.0.1:8000/docs

A small overview:

FastAPI autogenerated documentation

Is your app fully functionnal locally ? Perfect, we are ready for the rest.

Step 3 - Adapt your FastAPI application with the AWS Lambda ecosystem

To make your application compatible with Lambda, we will use a small package called Mangum.

Add it in your requirements.txt file, it will be essential in the next steps.

fastapi==0.63.0 uvicorn==0.13.4 mangum==0.11.0

We will modify main.py to wrap up the app object:

from fastapi import FastAPI from mangum import Mangum app = FastAPI(title="MyAwesomeApp") @app.get("/hello") def hello_world(): return {"message": "Hello World"} handler = Mangum(app)

Note:

At this point, the app is ready and will be functionnal with Lambda, but one of the features won't be accessible: the autogenerated documentation.

Indeed, when deployed, the application will be accessible with an endpoint that has this shape: https://XXXXXXX.execute-api.eu-west-1.amazonaws.com/stage.

Out, to generate the documentation, FastAPI needs, by default, to be served through a domain, not a path.

To solve this problem, we will modify the python code:

import os from fastapi import FastAPI from mangum import Mangum stage = os.environ.get('STAGE', None) openapi_prefix = f"/{stage}" if stage else "/" app = FastAPI(title="MyAwesomeApp", openapi_prefix=openapi_prefix) # Here is the magic @app.get("/hello") def hello_world(): return {"message": "Hello World"} handler = Mangum(app)

In summary:

We changed the path that is used to serve openapi.json, that will allow the documentation page to be generated without any issue.

At this point, your python code is ready ! Let's deploy that with serverless.

Step 4 - Use serverless to deploy the application

You need to install the serverless CLI and configure it with your AWS credentials.

Install the CLI with npm:

npm install -g serverless

It's a tool that facilitate the deployment of applications and services in the cloud, by describing them in a YAML file.

It's compatible with AWS Lambda, and more generally with AWS Cloudformation.

For more info about this tool, go check serverless.com.

Configure serverless with your credentials:

serverless config credentials --provider aws --key <YOUR_KEY> --secret <YOUR_SECRET_KEY>

Let's create a serverless.yaml file that contains all the specifications of our app:

service: my-awesome-app package: individually: true provider: name: aws runtime: python3.8 region: eu-west-1 stage: ${opt:stage, "dev"} plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: true layer: name: my-awesome-app-layer description: My awesome app layer compatibleRuntimes: - python3.8 functions: app: package: include: - "main.py" exclude: - "requirements.txt" - "package.json" - "package-lock.json" - ".serverless/**" - ".virtualenv/**" - "node_modules/**" handler: main.handler environment: STAGE: ${self:provider.stage} layers: - { Ref: PythonRequirementsLambdaLayer } events: - http: method: any path: /{proxy+}

And, at the same level, create a package.json file:

{ "name": "my-awesome-app", "version": "1.0.0", "author": "<YOUR_NAME>", "dependencies": { "serverless-python-requirements": "^5.0.1" } }

This file will help us install some JavaScript plugins that are used in our YAML file.

Install these plugins:

npm install

And, let's deploy our application !

sls deploy --stage staging # "stage" indicates which stage you are deploying. It could be anything.

serverless does its magic.

After some minutes (no more than 5 usually), you will see some logs like these:

Service Information service: my-awesome-app stage: staging region: eu-west-1 stack: my-awesome-app-staging resources: 12 api keys: None endpoints: ANY - <ENDPOINT HERE> functions: app: my-awesome-app-staging-app ...

By checking these logs, you will find your application endpoint !

Here, it's https://XXXXXXXXX.execute-api.eu-west-1.amazonaws.com/staging/ (XXXXXX is usually a mix of numbers and letters).

If you visit https://XXXXXXXXX.execute-api.eu-west-1.amazonaws.com/staging/docs, you will see your application documentation !

To update your app (e.g add a new route, etc.), you just need to modify your code, and deploy again:

sls deploy --stage staging

To completely delete your application from the cloud:

sls remove --stage staging

You now have all the information to create FastAPI apps and host them on AWS Lambda !

[Bonus] Step 5 - Use your own domain with your app

If you want your application to be available through your own domain, you're a few steps from doing it !

Some prerequisites are necessary:

  • a domain name hosted on AWS Route53
  • a certificate linked to this domain (for HTTPS), generated with AWS Certificate Manager (if you don't have it, create a new one and host it on the AWS default region us-east-1, as it is considered as global for certificates).

We will use a plugin to make our life easier: serverless-domain-manager.

Install it:

npm install serverless-domain-manager

Add it in the list of plugins, in your serverless.yaml file:

plugins: - serverless-python-requirements - serverless-domain-manager

We need to configure the plugin, so add these few lines in the custom section of serverless.yaml:

custom: customDomain: domainName: YOUR_DOMAIN_HERE stage: ${self:provider.stage} certificateArn: YOUR_CERTIFICATE_ARN_HERE endpointType: "edge" securityPolicy: tls_1_2 apiType: rest autoDomain: true autoDomainWaitFor: 120

(Don't forget to fill the fields with your own values).

Once it's done, deploy:

sls deploy --stage staging

After a few minutes, we can access to our app with our own domain !

... Really ?

Try to access to the documentation page located at /docs, you see the trap ?

FastAPI documentation error

This error occurs because of what we did previously to access the documentation page with the lambda default endpoint.

We just need to rollback:

app = FastAPI(title="MyAwesomeApp", openapi_prefix=openapi_prefix) # Change this app = FastAPI(title="MyAwesomeApp") # With this

Deploy again.

Tada GIF

You now have your application running on AWS Lambda, with your own domain !

Conclusion

Through this article written in the form of a tutorial, you are now capable of creating a FastAPI application, test it locallly and deploy it on the cloud !

If you have any questions, feel free to contact me. Don't hesitate !

See you soon,

Adem.