Build a Task Manager REST API
Build and deploy a Python REST API with FastAPI and AWS Lambda.
Introduction
30 Second Summary
Every time you order food through an app, check the weather, or scroll social media, your phone is talking to a REST API behind the scenes. These APIs are the backbone of modern software, and knowing how to build one is the single most in-demand backend skill for developers.
In this project, you will build a Task Manager API from scratch using Python and deploy it live on AWS so anyone on the internet can use it.
What You'll Build
A complete REST API with full CRUD (Create, Read, Update, Delete) endpoints, built with FastAPI and deployed as a serverless function on AWS Lambda.
By the end of this project, you'll have:
- A working REST API with 6 endpoints that handles creating, reading, updating, and deleting tasks.
- A live public URL serving your API on the internet via AWS Lambda.
- Hands-on experience with Python type hints, Pydantic data models, and auto-generated interactive API documentation.
- Secret Mission: Add filtering by status and search by title to your task list endpoint.
Are there any prerequisites?
None. This guide walks you through installing every tool from scratch, including Python, VS Code, and the AWS CLI. You just need a Windows PC and an internet connection.
Set Up Your Development Environment
Before you can build and deploy an API, you need three tools on your machine: a programming language, a code editor, and a way to talk to the cloud.
In this step, you will install Python (the language your API is written in), VS Code (the editor you will write code in), and the AWS CLI (the command-line tool that lets you deploy to Amazon's cloud).
In this step, get ready to:
- Install Python 3.13 and verify it works from your terminal.
- Install VS Code with the Python extension for code editing.
- Install the AWS CLI for deploying to the cloud later.
Install Python 3.13
Python is the programming language you will use to build your API. You need version 3.13 specifically because that is the version AWS Lambda supports for running your code in the cloud.
- Open your browser and go to the Python 3.13.14 release page:
- https://www.python.org/downloads/release/python-31314/
- Scroll down to the files table and click Windows installer (64-bit) (marked as Recommended).
- Run the downloaded installer.
- On the first screen of the installer, check the box that says Add python.exe to PATH. This is critical.
- Click Install Now and wait for the installation to complete.
- Click Close when the installer finishes.
Why Add python.exe to PATH?
PATH is a list of folders your terminal searches when you type a command. Without checking this box, typing python in your terminal would return "command not found" because Windows would not know where to look for it.
- Open a brand new terminal by pressing the Windows key, typing cmd, and pressing Enter.
- Check your Python version by running:
python --version
You should see a version number starting with Python 3.13.
✔️ I see Python 3.13.x
Python 3.13 is installed and ready to go. Move on to the next substep.
ⓧ I see a different version number
You may have an older Python installation on your machine that is taking priority. You need Python 3.13 for Lambda compatibility.
- Open Settings on your PC.
- Select Apps, then Installed apps.
- Search for any existing Python installations and uninstall them.
- Reinstall Python 3.13.14 from the link above, making sure to check Add python.exe to PATH.
- Close your terminal and open a new one, then run python --version again.
ⓧ Command not found
This means either the installer did not run, or the PATH checkbox was not checked.
- Download the installer again from the link above.
- Run it and make sure Add python.exe to PATH is checked before clicking Install Now.
- Close your terminal completely and open a new one (the old terminal does not know about PATH changes).
- Run python --version again.
Install VS Code and the Python extension
VS Code is a free code editor that gives you syntax highlighting, error detection, and a built-in terminal. The Python extension adds smart features like auto-completion for Python code.
- Open your browser and go to https://code.visualstudio.com.
- Click the large Download for Windows button.
- Run the downloaded installer and accept the default options.
- Once installed, open VS Code by pressing the Windows key, typing VS Code, and pressing Enter.
Now install the Python extension so VS Code understands your Python code.
- Press Ctrl+Shift+X to open the Extensions panel.
- Type Python in the search bar.
- Find the extension named Python by Microsoft (it has millions of downloads and appears at the top).
- Click Install.
How do I know it worked?
After installation, you should see the Python extension listed in your Extensions panel with a green checkmark or a gear icon. The extension activates automatically when you open any .py file.
Install the AWS CLI
The AWS CLI is a command-line tool that lets you interact with Amazon Web Services from your terminal. You will use it later to configure your credentials so you can deploy your API to the cloud.
- Download the AWS CLI installer for Windows by opening this link in your browser:
- https://awscli.amazonaws.com/AWSCLIV2.msi
- Run the downloaded AWSCLIV2.msi file.
- Accept the license agreement and click Next through each screen, keeping the default settings.
- Click Install, then Finish when it completes.
- Close any open terminal windows (the old terminal does not know the AWS CLI was just installed).
- Open a new terminal by pressing the Windows key, typing cmd, and pressing Enter.
- Verify the AWS CLI is installed by running:
aws --version
You should see output starting with aws-cli/2. followed by a version number.
✔️ I see aws-cli/2.x.x
The AWS CLI is installed and ready. You are all set for deployment later in the project.
ⓧ Command not found
The installer may not have completed, or your terminal was not restarted.
- Make sure the MSI installer ran to completion (you saw the Finish button).
- Close ALL open terminal windows and open a brand new one.
- Run aws --version again.
- If it still does not work, restart your PC and try again. Windows sometimes needs a restart to pick up new PATH entries from MSI installers.
Your development environment is ready. Next up, you will create a project folder and write your first API endpoint.
Build Your First FastAPI App
Your development tools are installed and ready to go. Now it's time to write actual code.
In this step, you'll create a project folder, set up an isolated Python environment, and build your first REST API endpoint using FastAPI.
In this step, get ready to:
- Create a project folder and virtual environment.
- Write your first API endpoint with FastAPI.
- Run and test your API locally in the browser.
Create your project folder and install dependencies
Before writing any code, you need a dedicated folder for this project and an isolated Python environment to keep your packages separate from your system Python.
- Press the Windows key to open your search bar.
- Type VS Code and press Enter to open it.
- Open the built-in terminal by pressing Ctrl+` (the backtick key, above Tab).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir task-manager-api
cd task-manager-api
- Confirm the folder exists by running:
ls
You should see an empty directory listing (no files yet).
A virtual environment gives your project its own isolated set of Python packages, separate from anything else on your machine.
- Create and activate a virtual environment by running these commands:
python -m venv venv
venv\Scripts\activate
- Confirm the virtual environment is active by checking your terminal prompt.
You should see (venv) at the beginning of your terminal prompt.
Not seeing (venv) in your prompt?
If you see an error about scripts being disabled, your PowerShell execution policy needs updating.
- Run this command to allow activation scripts:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
- Then try the activation command again:
venv\Scripts\activate
help me activate my Python virtual environment on Windows
- Install the three packages your project needs by running:
pip install fastapi uvicorn mangum
What are these packages?
- fastapi is the web framework you'll use to define your API endpoints.
- uvicorn is a local development server that runs your FastAPI app.
- mangum is an adapter that makes your FastAPI app compatible with AWS Lambda for deployment later.
- Wait for the installation to finish.
You should see a Successfully installed message listing all the packages and their dependencies.
Seeing a pip error?
Make sure you see (venv) in your prompt before running pip install. If the virtual environment is not active, packages may fail to install correctly.
help me fix a pip install error in my virtual environment
Write your first API endpoint
With your packages installed, you can now create the Python file that defines your API. This single file will hold your app, your endpoints, and the Lambda adapter.
- In the VS Code Explorer sidebar (the top icon on the left), click Open Folder.
- Navigate to your Desktop, select the task-manager-api folder, and click Select Folder.
- Create a new file by clicking the New File icon in the Explorer sidebar (or press Ctrl+N).
- Save the file immediately as main.py by pressing Ctrl+S and entering the filename.
- Add the following code to main.py:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from mangum import Mangum
# Create the FastAPI app instance
app = FastAPI(title="Task Manager API")
# Root endpoint - returns a health-check message
@app.get("/")
def root():
return {"message": "Task Manager API is running"}
# Mangum wraps FastAPI for AWS Lambda compatibility
handler = Mangum(app, lifespan="off")
What does this code do?
- The imports bring in FastAPI (the framework), Pydantic (for data validation in later steps), and Mangum (the Lambda adapter).
- app = FastAPI(title="Task Manager API") creates your application instance. All your endpoints attach to this object.
- @app.get("/") is a decorator that tells FastAPI to respond to GET requests at the root URL.
- handler = Mangum(app, lifespan="off") wraps your app so AWS Lambda can invoke it. The lifespan="off" setting lets Lambda manage the app lifecycle.
- Save main.py by pressing Ctrl+S.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete main.py at this point:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from mangum import Mangum
app = FastAPI(title="Task Manager API")
@app.get("/")
def root():
return {"message": "Task Manager API is running"}
handler = Mangum(app, lifespan="off")
Run your API locally
Your code is written. Time to start the local development server and see your API respond in the browser.
- In your VS Code terminal (make sure you still see (venv) in the prompt), start the server by running:
uvicorn main:app --reload
What does this command do?
- main:app tells uvicorn to look in main.py for the object called app.
- --reload enables auto-reload. Every time you save a change to your code, the server restarts automatically.
You should see output ending with a line like Uvicorn running on http://127.0.0.1:8000 and Application startup complete.
- Open your web browser and navigate to http://127.0.0.1:8000.
You should see the JSON response: {"message":"Task Manager API is running"}.
- Now navigate to http://127.0.0.1:8000/docs in your browser.
This is the interactive Swagger documentation that FastAPI auto-generates from your code. You can see your GET endpoint listed and even test it directly from this page.
Why is auto-generated documentation useful?
FastAPI reads your Python type hints and route definitions, then builds a full interactive API reference for free. Anyone who visits /docs can see every endpoint, try requests, and inspect responses without reading your source code.
Browser showing an error?
Make sure the server is still running in your terminal. You should see the Uvicorn running message. If you accidentally closed the terminal, re-open it, activate the virtual environment with venv\Scripts\activate, and run the uvicorn command again.
Also check you typed the URL exactly: http://127.0.0.1:8000 (not https).
help me troubleshoot my FastAPI server not responding
Your API is alive and responding to requests on your local machine. Next up, you'll add full CRUD endpoints to create, read, update, and delete tasks.
Add Task CRUD Endpoints
Your FastAPI app is running locally and responding to requests. Now it's time to build the real functionality: a full set of endpoints that let users create, view, update, and delete tasks.
CRUD stands for Create, Read, Update, and Delete. These four operations are the backbone of almost every REST API. You'll define data models to validate incoming requests, then build each endpoint one by one.
In this step, get ready to:
- Define Pydantic data models and set up in-memory task storage.
- Build the Create (POST) and Read (GET) endpoints.
- Build the Update (PUT) and Delete (DELETE) endpoints.
Define your data models and storage
Before you can create or manage tasks, you need to define what a task looks like. Pydantic models tell FastAPI exactly which fields to expect in incoming requests and what types they should be.
- In main.py, find the line app = FastAPI(title="Task Manager API").
- Add the following code below it, above the @app.get("/") decorator:
# In-memory storage for tasks
tasks = {}
task_counter = 0
# Model for creating a new task - validates incoming POST data
class TaskCreate(BaseModel):
title: str
description: str = ""
status: str = "pending"
# Model for updating a task - all fields optional
class TaskUpdate(BaseModel):
title: str | None = None
description: str | None = None
status: str | None = None
What does this code do?
tasks is a Python dictionary that stores all your tasks in memory. task_counter tracks the next available ID so every task gets a unique number.
TaskCreate defines what data is required when creating a task: a title (required), a description (optional, defaults to empty), and a status (optional, defaults to "pending"). TaskUpdate makes every field optional so you can update just one field at a time.
- Save main.py.
- Check your terminal where uvicorn is running. You should see the server reload without any errors.
Seeing a syntax error in the terminal?
- Make sure you placed the new code below app = FastAPI(title="Task Manager API") and above @app.get("/"). The models must be defined before any endpoint that uses them.
- Check that the str | None = None syntax has the pipe character (|) not a lowercase L. This union syntax requires Python 3.10 or higher.
Why won't my Pydantic models load?
Build the Create and Read endpoints
With your models in place, you can now build the endpoints that create and retrieve tasks. These cover the C and R in CRUD.
- In main.py, find the root() function. Add the following code below it, above the handler = Mangum(app, lifespan="off") line:
@app.get("/tasks")
def get_tasks():
# Return all tasks as a list
return list(tasks.values())
@app.get("/tasks/{task_id}")
def get_task(task_id: int):
# Return 404 if the task doesn't exist
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
return tasks[task_id]
What does this code do?
- GET /tasks returns every task in your dictionary as a JSON list.
- GET /tasks/{task_id} looks up a single task by its numeric ID. If the ID doesn't exist, it raises an HTTPException with a 404 status code so the caller gets a clear error message.
- Now add the POST endpoint directly below the get_task() function you just wrote:
@app.post("/tasks", status_code=201)
def create_task(task: TaskCreate):
global task_counter
# Generate a unique ID for the new task
task_counter += 1
# Convert the Pydantic model to a dictionary and add the ID
new_task = {"id": task_counter, **task.model_dump()}
tasks[task_counter] = new_task
return new_task
What does this code do?
- POST /tasks accepts a JSON body matching your TaskCreate model. FastAPI automatically validates the request body against the model.
- task.model_dump() converts the Pydantic model into a plain Python dictionary. The ** unpacks it so you can merge it with the generated ID.
- status_code=201 tells FastAPI to return a "201 Created" HTTP status instead of the default 200, which is the standard response for successfully creating a resource.
- Save main.py.
- Open http://127.0.0.1:8000/docs in your browser. You should see the GET /tasks, GET /tasks/{task_id}, and POST /tasks endpoints listed alongside the original GET / endpoint.
Test your POST endpoint now
Click the POST /tasks section in the docs page, then click Try it out. Replace the example JSON body with something like {"title": "Buy groceries"} and click Execute. You should see a 201 response with your new task including an id of 1.
Endpoints not showing up in /docs?
- Make sure you placed the new endpoints below the root() function and above the handler = Mangum(app, lifespan="off") line. The handler must stay at the very bottom of the file.
- Check that uvicorn reloaded successfully. If it crashed, look at the error message in your terminal for a line number and fix the typo.
Why aren't my new endpoints appearing in the FastAPI docs?
Build the Update and Delete endpoints
The final two endpoints handle the U and D in CRUD. These let users modify an existing task or remove it entirely.
- In main.py, add the following code below the create_task() function, above the handler line:
@app.put("/tasks/{task_id}")
def update_task(task_id: int, task_update: TaskUpdate):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
existing = tasks[task_id]
# Only update fields that were actually sent in the request
update_data = task_update.model_dump(exclude_unset=True)
existing.update(update_data)
return existing
What does this code do?
- PUT /tasks/{task_id} takes both a path parameter (the task ID) and a JSON body matching TaskUpdate.
- model_dump(exclude_unset=True) only includes fields the caller actually sent. This means sending {"status": "done"} updates the status without touching the title or description.
- Now add the DELETE endpoint directly below update_task():
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
# Remove the task and return it in the response
deleted = tasks.pop(task_id)
return {"message": "Task deleted", "task": deleted}
What does this code do?
DELETE /tasks/{task_id} removes the task from the dictionary using tasks.pop(task_id) and returns the deleted task in the response so the caller can confirm which task was removed.
- Save main.py.
- Refresh http://127.0.0.1:8000/docs in your browser. You should now see all 6 endpoints listed: GET /, GET /tasks, GET /tasks/{task_id}, POST /tasks, PUT /tasks/{task_id}, and DELETE /tasks/{task_id}.
- Test the full flow: create a task with POST, retrieve it with GET by ID, update its status with PUT, list all tasks with GET, then delete it with DELETE.
Getting a 404 when testing GET or PUT?
- Tasks are stored in memory, so they disappear every time the server reloads. If uvicorn auto-reloaded after a file save, you need to create a new task with POST before you can GET, PUT, or DELETE it.
- Make sure the task ID you're using in the URL matches the id returned by the POST response. IDs start at 1 and increment.
Why am I getting a 404 error when accessing a task by ID?
✔️ Awesome, I've got everything!
Great. Double check you've saved main.py and that all 6 endpoints are visible in the /docs page.
ⓧ I'd like to double check the full code
Here's the complete main.py at this point:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from mangum import Mangum
app = FastAPI(title="Task Manager API")
tasks = {}
task_counter = 0
class TaskCreate(BaseModel):
title: str
description: str = ""
status: str = "pending"
class TaskUpdate(BaseModel):
title: str | None = None
description: str | None = None
status: str | None = None
@app.get("/")
def root():
return {"message": "Task Manager API is running"}
@app.get("/tasks")
def get_tasks():
return list(tasks.values())
@app.get("/tasks/{task_id}")
def get_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
return tasks[task_id]
@app.post("/tasks", status_code=201)
def create_task(task: TaskCreate):
global task_counter
task_counter += 1
new_task = {"id": task_counter, **task.model_dump()}
tasks[task_counter] = new_task
return new_task
@app.put("/tasks/{task_id}")
def update_task(task_id: int, task_update: TaskUpdate):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
existing = tasks[task_id]
update_data = task_update.model_dump(exclude_unset=True)
existing.update(update_data)
return existing
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
deleted = tasks.pop(task_id)
return {"message": "Task deleted", "task": deleted}
handler = Mangum(app, lifespan="off")
Your API now handles all four CRUD operations locally. Next up, you'll package this code and deploy it to AWS Lambda so anyone on the internet can access your endpoints.
Deploy to AWS Lambda
Your API works perfectly on your own machine, but nobody else can reach it yet. To share it with the world, you need to deploy it somewhere publicly accessible on the internet.
AWS Lambda runs your code without you managing any servers. In this step, you'll set up your AWS credentials, package your app for Lambda's Linux environment, and create a live public URL for your API.
In this step, get ready to:
- Set up an AWS account and configure CLI access.
- Package your FastAPI app with Linux-compatible dependencies.
- Create a Lambda function and get a public Function URL.
Set up your AWS account and configure the CLI
Before you can deploy anything, you need an AWS account and a way for your local terminal to authenticate with AWS services.
- Open your browser and go to aws.amazon.com.
- Click Create an AWS Account and follow the prompts to register.
- You will need a credit card for identity verification, but you will not be charged for this project.
Will I be charged?
No. AWS Lambda has an Always Free tier that covers 1 million requests per month and 400,000 GB-seconds of compute per month. This project will use a negligible fraction of those limits.
New AWS customers also receive up to $200 in free tier credits.
Now you need an IAM user so your terminal can talk to AWS securely.
- Sign in to the AWS Management Console with your new account.
- In the top search bar, type IAM and select the IAM service.
- In the left navigation pane, click Users.
- Click Create user.
- Enter a user name (for example, lambda-deployer) and click Next.
- On the Set permissions page, select Attach policies directly.
- Search for AdministratorAccess and check the box next to it.
- Click Next, then click Create user.
Next, create an access key so your CLI can authenticate as this user.
- Click on the user name you just created to open the user details page.
- Select the Security credentials tab.
- In the Access keys section, click Create access key.
- On the best practices page, select Command Line Interface (CLI) as your use case.
- Check the confirmation box at the bottom and click Next.
- Click Create access key.
- You will see your Access Key ID and Secret Access Key. Click Download .csv file and save it somewhere safe.
Save your credentials now
This is the only time AWS shows you the Secret Access Key. If you close this page without saving it, you will need to create a new access key.
Now configure the AWS CLI to use these credentials.
- Return to your terminal in VS Code (make sure your virtual environment is still active) and run this command:
aws configure
- The command will prompt you for four values. Enter them one at a time:
- AWS Access Key ID: paste your Access Key ID from the CSV file.
- AWS Secret Access Key: paste your Secret Access Key from the CSV file.
- Default region name: type ap-southeast-2 (this is the Sydney region).
- Default output format: press Enter to accept the default.
What does aws configure do?
This command saves your credentials locally so the AWS CLI can authenticate your requests. It stores them in a file at ~/.aws/credentials on your machine.
The region ap-southeast-2 means your Lambda function will be hosted in the Sydney data centre, which gives the lowest latency for Australian users.
- Verify the configuration worked by running:
aws sts get-caller-identity
You should see a JSON response containing your Account ID, user ARN, and the user name you created.
Seeing an error about credentials?
Make sure you opened a new terminal after installing the AWS CLI. If the aws command is not found, close your terminal and open a fresh one.
If you see "Invalid credentials", double-check you pasted the Access Key ID and Secret Access Key correctly. You can re-run aws configure to enter them again.
Why is my aws configure not working?
Package your app for Lambda
AWS Lambda runs on Linux, but your machine runs Windows. You need to install your Python dependencies in a way that produces Linux-compatible files.
- Install your dependencies targeting Linux by running this command:
pip install --platform manylinux2014_x86_64 --implementation cp --python-version 3.13 --only-binary=:all: --target package fastapi mangum
Why the extra flags?
Normally pip install downloads packages matching your current OS (Windows). The --platform manylinux2014_x86_64 flag forces pip to download Linux-compatible versions instead.
The --target package flag installs everything into a folder called package/ rather than your virtual environment. This keeps your deployment bundle separate from your local setup.
- Confirm the package folder was created by running:
ls package
You should see folders like fastapi, mangum, pydantic, and several other dependency folders.
Seeing an error about no matching distribution?
Make sure your virtual environment is still active (you should see (venv) in your terminal prompt). If it is not, re-activate it with venv\Scripts\activate.
Help me fix a pip install error when packaging for Lambda
Now copy your application code into the package folder and create a zip file.
- Copy main.py into the package folder by running:
Copy-Item main.py package\
- Create the deployment zip file by running:
Compress-Archive -Path package\* -DestinationPath deployment.zip
- Confirm deployment.zip was created by running:
ls deployment.zip
You should see deployment.zip listed in the output.
Compress-Archive failed?
If you see an error about the file already existing, delete the old zip first with Remove-Item deployment.zip and run the Compress-Archive command again.
Help me troubleshoot creating my Lambda deployment zip
Create the Lambda function and get a public URL
Your deployment package is ready. Now you will create the Lambda function in the AWS Console and upload your zip file.
- Open your browser and go to the AWS Console at console.aws.amazon.com.
- In the top search bar, type Lambda and select the Lambda service.
- Make sure your region is set to ap-southeast-2 (Asia Pacific - Sydney) in the top-right dropdown.
- Click Create function.
- Select Author from scratch.
- For Function name, enter task-manager-api.
- For Runtime, select Python 3.13.
- Leave all other settings at their defaults and click Create function.
You should see a success message and the function overview page.
Now upload your deployment package.
- On the function page, scroll down to the Code tab.
- Click the Upload from dropdown and select .zip file.
- Click Upload, navigate to your project folder, and select deployment.zip.
- Click Save.
Next, update the handler setting so Lambda knows which function to call.
- Scroll down to Runtime settings and click Edit.
- Change the Handler field to main.handler.
- Click Save.
What is main.handler?
This tells Lambda to look in main.py and call the function named handler. That's the Mangum wrapper you defined at the bottom of your file with handler = Mangum(app, lifespan="off").
Finally, create a public URL for your function.
- Click the Configuration tab.
- In the left sidebar, click Function URL.
- Click Create function URL.
- For Auth type, select NONE.
- Click Save.
Your Function URL now appears in the Function overview section at the top of the page. It ends in .lambda-url.ap-southeast-2.on.aws.
Why auth type NONE?
Setting auth type to NONE means anyone with the URL can call your API without credentials. This is appropriate for a learning project that demonstrates a public REST API.
In production, you would use AWS_IAM authentication or add your own auth logic inside the API.
- Copy the Function URL. You will use it in the next step to test your live API.
Don't see the Function URL option?
Make sure you are on the Configuration tab (not the Code tab). The Function URL option is in the left sidebar under Configuration.
Also confirm your region is ap-southeast-2 in the top-right corner. If you created the function in a different region, you won't see it here.
I can't find the Function URL option in my Lambda console
Your API is now live on the internet. Next up, you'll test all your endpoints against the public URL to confirm everything works.
Test Your Live API
Your API is deployed and has a public URL. Anyone on the internet can now send requests to it, just like any production backend service.
In this step, you will hit your Function URL from a browser and from PowerShell to confirm every endpoint works live. This is the moment where your local project becomes a real, internet-facing API.
In this step, get ready to:
- Verify the health-check and interactive docs are publicly accessible.
- Test all CRUD operations against your live Function URL.
Open your Function URL in a browser
Your Function URL is the public address AWS Lambda generated for your API. It ends in .lambda-url.ap-southeast-2.on.aws.
- Open a new browser tab.
- Paste your Function URL into the address bar and press Enter.
You should see the JSON health-check response from your root endpoint.
What should I see?
Your browser displays the JSON object {"message":"Task Manager API is running"}. This confirms your Lambda function is executing your FastAPI code and returning responses to the internet.
- Now add /docs to the end of your Function URL and press Enter.
You should see the Swagger UI interactive documentation page, now publicly accessible.
Why is /docs important?
FastAPI auto-generates this interactive documentation from your code. Anyone with your URL can see your API's endpoints, their parameters, and even test them directly. This is the kind of developer experience that impresses hiring managers.
Seeing an Internal Server Error?
If you see a 500 error or {"message":"Internal Server Error"}, go back to the AWS Console, open your Lambda function, and check the Monitor tab. Click View CloudWatch logs to see the actual error. Common causes include the Handler not being set to main.handler or main.py not being at the root of your zip file.
help me debug my Lambda Internal Server Error
Test all CRUD operations against your live URL
The /docs page is the easiest way to test, but you can also use PowerShell's Invoke-RestMethod to send requests from your terminal. Below you will test each endpoint using PowerShell to prove the API works end-to-end.
- Open a new PowerShell terminal in VS Code by pressing Ctrl+`.
- Create a task by running this command (replace the URL with your own Function URL):
$body = @{ title = "Buy groceries"; description = "Milk, eggs, bread"; status = "pending" } | ConvertTo-Json
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks" -Method Post -Body $body -ContentType "application/json"
What does this command do?
This sends an HTTP POST request with a JSON body to your /tasks endpoint. ConvertTo-Json converts the PowerShell hashtable into a JSON string. -ContentType "application/json" tells the server the body is JSON.
You should see the created task returned with an id of 1 and status pending.
- List all tasks by running:
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks"
You should see an array containing the task you just created.
- Get a single task by its ID:
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks/1"
You should see the full details of task 1.
- Update the task's status to done by running:
$update = @{ status = "done" } | ConvertTo-Json
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks/1" -Method Put -Body $update -ContentType "application/json"
You should see the task returned with status now showing done.
- Delete the task by running:
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks/1" -Method Delete
You should see a confirmation message with "message": "Task deleted" and the deleted task's details.
- Confirm the task is gone by listing all tasks one more time:
Invoke-RestMethod -Uri "[[FUNCTION_URL="your-function-url.lambda-url.ap-southeast-2.on.aws"]]/tasks"
You should see an empty array, confirming the delete worked.
What just happened?
You exercised the full CRUD lifecycle: Create (POST), Read (GET), Update (PUT), and Delete (DELETE). Each request travelled over the internet to your Lambda function, which ran your FastAPI code and returned the JSON response.
This is exactly how real production APIs work. The only difference between your project and a startup's backend is the storage layer (yours is in-memory, theirs is a database).
Getting a 404 or connection error?
Make sure you are using your full Function URL (starting with https://) and that you have not added a trailing slash before the path. The URL format should look like https://abc123.lambda-url.ap-southeast-2.on.aws/tasks.
If you see a timeout, check that your Function URL's auth type is set to NONE in the Lambda Console under Configuration > Function URL.
help me fix a connection error when calling my Lambda Function URL
You now have a fully working REST API live on the internet. Anyone with your URL can create, read, update, and delete tasks. Next up, you will tackle the Secret Mission and add filtering and search to make this API even more useful.
Secret mission
Add Filtering and Search
Your API returns every single task when someone hits GET /tasks. Real-world APIs let users narrow down results with filters and search. Can you add status filtering and title search to your task list endpoint?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. The Lambda free tier is Always Free (not time-limited), so this project costs nothing to leave running.
Cost warning
The AWS Lambda Always Free tier covers 1 million requests per month and 400,000 GB-seconds of compute per month permanently. For this project, you will use a negligible fraction of these limits. You will never be charged for leaving this function running.
Resources you used:
- AWS Lambda function (task-manager-api) in ap-southeast-2.
- IAM user with programmatic access keys.
- Local project folder on your Desktop (main.py, package/, deployment.zip, venv/).
Keep everything running
No action needed. Choose this if you are still actively building or want to keep experimenting with your API.
- Your Lambda function costs nothing while idle. It only runs when someone hits the URL.
- Your IAM user remains configured for CLI access if you want to do more AWS projects.
- Your local project files stay on your Desktop for future reference.
Pause - I'll come back to this later
Since Lambda is serverless, there is nothing to "pause". The function only runs when called, so it already costs nothing at rest.
- If your local dev server is still running (uvicorn), press Ctrl+C in the terminal to stop it.
- Deactivate your virtual environment by running deactivate in your terminal.
- Your Lambda function and IAM user can stay as they are with no ongoing cost.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each step below.
Delete the Lambda function:
- Open the AWS Console and navigate to the Lambda service.
- On the Functions page, select task-manager-api.
- Click Actions, then Delete.
- Type confirm in the text input field and click Delete.
Delete the IAM user:
- Open the IAM Console and click Users in the left navigation pane.
- Select the checkbox next to the IAM user you created for this project.
- Click Delete at the top of the page.
- Enter the username in the confirmation dialog and click Delete.
Delete local project files:
- Delete your project folder by running this command in PowerShell:
Remove-Item -Recurse -Force ~\Desktop\task-manager-api
What about the Lambda execution role?
When you created the Lambda function in the console, AWS also created an execution role for it. To fully clean up, navigate to IAM > Roles, find the role starting with task-manager-api-role, select it, click Delete, and confirm by entering the role name.
That's a Wrap!
That's a Wrap!
Nice work! You just built and deployed a production-style REST API from scratch using Python and AWS Lambda.
You've learned how to:
- Build a complete REST API with full CRUD operations (Create, Read, Update, Delete) using FastAPI and Pydantic data validation.
- Deploy a Python application to AWS Lambda as a serverless function with a live public URL anyone can access.
- Use Python type hints and structured models to validate request and response data automatically.
- Secret Mission: Added filtering and search query parameters to handle real-world data retrieval patterns.
Ready to quiz yourself?