Building Backend APIs with FastAPI on AWS Lambda
One of the trade-offs of working in the cloud is the risk of vendor lock-in. While cloud platforms offer powerful tools and scalability, they can also tie you into their ecosystem, making it harder to pivot or migrate down the road.
When it comes to building backend APIs in Python, you have a few great frameworks at your disposal—Django, FastAPI, and Flask—each with its own level of complexity and opinionation. Django is feature-rich and great for full-stack apps, Flask is minimal and flexible, but FastAPI stands out as a modern, async-first framework that makes it incredibly easy to build fast, clean, and production-ready APIs.
FastAPI is built on top of Starlette and Pydantic, which means it’s both high-performance and deeply integrated with Python’s type system. With automatic data validation, interactive documentation (via Swagger and ReDoc), and a strong developer experience, it’s quickly becoming a go-to choice for building APIs in Python.
If you’d like to follow along with the code or explore the full project, you can find the source on GitHub here.
In this post, we’ll walk through how to deploy a FastAPI backend on AWS Lambda, using Lambda Layers and the AWS Serverless Application Model (SAM). This approach lets you combine the speed and elegance of FastAPI with the scalability and cost-efficiency of serverless architecture.
To streamline development and packaging, we’ll use uv
, a blazing-fast Python package manager and virtual environment tool. Make sure you have it installed before diving in.
By the end of this guide, you’ll have a working FastAPI app deployed to AWS Lambda with a clean, modular directory structure that looks like this:
|
|
Architecture Overview
Our backend is built using:
- API Gateway – routes HTTP requests
- Lambda – runs FastAPI with Mangum
- Lambda Layers – share dependencies
- DynamoDB – stores data
End-to-End Flow
- A client sends an HTTP request.
- API Gateway forwards it to the Lambda function.
- FastAPI handles the request, optionally interacting with DynamoDB.
- The response is sent back to the client.
Defining the Architecture with SAM
To bring our architecture to life, we define all our cloud infrastructure using AWS SAM (Serverless Application Model). This allows us to describe the entire system—API Gateway, Lambda function, Lambda Layer, and DynamoDB table—in a single declarative file: infra/infra.sam.yaml
.
Let’s walk through what this file sets up.
API Gateway – Exposing the FastAPI Backend
The first component is API Gateway, which acts as the frontend to our backend:
|
|
This sets up a REST API named MySimpleApi
, with a deployment stage called prod
. It forwards all requests (via the /{proxy+}
path) to our Lambda function. We’re not enforcing any authentication here (DefaultAuthorizer: NONE
) to keep things simple, but this can be expanded later with Cognito or custom authorizers.
Lambda Function – Running FastAPI
At the core is our Lambda function, where FastAPI is running:
|
|
This Lambda function is triggered by API Gateway for any HTTP method on any subpath using the /{proxy+} pattern. This is a catch-all route that matches any path except the root /. For example, /items, /v1/status, or /health would be captured by this route, but / would not. If you want the root path to be handled as well, you’ll need to define a separate event with Path: “/”.
The function uses a Lambda Layer (MyLib
) to include shared dependencies and logic, keeping the function code clean and maintainable. Environment variables, such as the table name (TABLE_NAME), are injected to configure the function at runtime.
It also has permission to interact with DynamoDB using the predefined DynamoDBCrudPolicy
, scoped to the MyTable
resource.
The FastAPI application is made Lambda-compatible using mangum
, an adapter that translates API Gateway events into ASGI requests, allowing FastAPI to run seamlessly within the Lambda execution environment.
Lambda Layer – Packaging Dependencies
To avoid bundling third-party dependencies directly in our function code, we use a Lambda Layer:
|
|
This keeps our deployment size lean and improves build speed. The layer is built using a Makefile
, making it easy to control and reproduce builds. It’s compatible with Python 3.13 and can be reused across multiple Lambda functions if needed.
DynamoDB – Simple, Scalable Storage
For persistence, we use a DynamoDB table:
|
|
This table stores items with an id
as the primary key. With PAY_PER_REQUEST
billing, there’s no need to provision capacity upfront—it scales automatically and you only pay for what you use.
Outputs – API URL at Your Fingertips
Finally, SAM gives us a convenient output:
|
|
Once deployed, this output provides the full URL where your FastAPI backend is publicly accessible.
Creating a Lambda Layer with a Reusable Library
To keep our architecture clean and modular, we’ll use a Lambda Layer to package shared dependencies and reusable code. This layer will contain:
- Third-party libraries like
fastapi
,mangum
, andboto3
- A custom Python library called
mylib
that encapsulates shared logic for accessing DynamoDB
Using a Lambda Layer helps us keep our actual function code lightweight and separates infrastructure concerns from application logic. It also makes shared utilities reusable across multiple Lambda functions.
Layer Directory Structure
Our layer will follow this structure:
|
|
This structure keeps the source code isolated in src/mylib
while letting SAM use the Makefile
to package everything for deployment.
Step 1: Initialize the Layer Project with uv
We’ll use uv
, a fast and modern Python package manager, to create and manage our layer.
Run the following commands from your project root:
|
|
This sets up a new Python library called mylib
, including a pyproject.toml
and a clean src/mylib
directory for our code.
Step 2: Install Shared Dependencies
Install the packages we want to share across Lambda functions:
|
|
These will be bundled into the layer so our application Lambda doesn’t need to install them separately.
Step 3: Add DynamoDB Logic to mylib
In layers/mylib/src/mylib/db.py
, define reusable functions to interact with DynamoDB:
|
|
These helper functions allow for clean separation of database logic from your main API code and can be reused across different Lambda functions.
Step 4: Add a Makefile
to Build the Layer
AWS SAM supports building Lambda Layers using a Makefile
. Add a file named Makefile
inside layers/mylib
with the following content:
|
|
This tells SAM how to:
- Create the target
python/
folder where Lambda expects dependencies - Copy your
mylib
source code into it - Install all dependencies using
uv
into that same directory
When you run sam build
, SAM will automatically invoke this target to package your layer correctly.
Building the API Lambda
Now that our Lambda Layer is ready with shared dependencies and DynamoDB helpers, it’s time to create the actual Lambda function that runs our FastAPI app. This function will handle HTTP requests routed from API Gateway and interact with DynamoDB using our mylib
module.
We’ll start by setting up a Python package for our Lambda function using uv
, and then build out the directory structure and code.
Step 1: Initialize the Lambda Project
First, create and initialize the Lambda project using uv
. This sets up a modern Python environment with a clean structure and dependency management:
|
|
Next, add the following dependencies:
|
|
This installs:
fastapi[standard]
: FastAPI and its optional extras (e.g.,pydantic
,uvicorn
)pytest
,pytest-xdist
: for running tests efficientlyrequests
: for making HTTP requests in testsmylib
: as an editable local dependency pointing to the code in our Lambda layer
Step 2: Create the Directory Structure
Let’s manually create the source structure for our Lambda API:
|
|
This structure mirrors the best practices for FastAPI projects—versioning your routes under v1/
while keeping the entry point (main.py
) clean and focused.
Here’s what your directory will look like:
|
|
Step 3: Define the /items
API Routes
Let’s implement the FastAPI routes to handle basic item operations. Open src/api_lambda/v1/items.py
and add the following:
|
|
Step 4: Add the FastAPI App Entry Point
Now, connect your router and configure FastAPI to run inside AWS Lambda using Mangum
. Open src/api_lambda/main.py
and add:
|
|
This wraps the FastAPI app using Mangum
, making it compatible with AWS Lambda and API Gateway events.
Deploying the Infrastructure
With our Lambda Layer and FastAPI code in place, it’s time to deploy the infrastructure to AWS using AWS SAM (Serverless Application Model).
SAM handles packaging your code, building the Lambda Layer with the Makefile
, provisioning API Gateway, Lambda, and DynamoDB—all from the definitions in our infra.sam.yaml
template.
Step 1: Build and Deploy with SAM
Navigate to the infra
directory where the SAM template is located:
|
|
Run the following command to build and deploy the stack:
|
|
Once confirmed, SAM will:
- Build the application artifacts
- Install dependencies using the Lambda Layer’s Makefile
- Package everything into a CloudFormation stack
- Deploy your infrastructure to AWS
Step 2: Get Your API Endpoint
After deployment, SAM will print out your API Gateway endpoint. It should look something like this:
|
|
This is your live FastAPI-powered API, running serverlessly on AWS Lambda behind API Gateway. You can now start interacting with it using any HTTP client.
API Testing
Now that your FastAPI backend is live, it’s time to verify that the endpoints are working as expected. We’ll write a few simple tests to check the core functionality of the /items
API.
These tests will cover:
- Creating a new item
- Retrieving an item by ID
- Deleting an item
- Listing all items
We’re using the requests
library (already added to your development dependencies) to make HTTP calls to the deployed API.
Test File Structure
Inside your api-lambda
project, create the following test file:
|
|
Add the API Tests
Paste the following code into api-lambda/tests/test_api.py
. Replace the placeholder URL with your actual deployed API Gateway endpoint (it should end with /items/
):
|
|
Running the Tests
To run your tests, make sure you’re in the api-lambda
directory and then execute:
|
|
This will run your tests in parallel using pytest-xdist
, and give you a fast and reliable check of your deployed API’s functionality.
Conclusion
In this post, we walked through building a modern, serverless FastAPI backend deployed on AWS Lambda using AWS SAM. Along the way, we:
- Created a reusable Lambda Layer with FastAPI and custom database utilities
- Built and deployed a FastAPI application that integrates with API Gateway and DynamoDB
- Used
uv
to manage Python environments and streamline development - Defined our infrastructure as code with a clean SAM template
- Wrote end-to-end API tests to validate that everything works as expected
By leveraging serverless technologies and modular code organization, we ended up with a lightweight, scalable, and cost-effective backend architecture—without managing a single server.
This setup provides a strong foundation for building production-ready APIs on AWS with Python.
Happy engineering!