Getting Started with FastAPI Using uv
FastAPI is one of the fastest-growing Python web frameworks, thanks to its speed, modern syntax, and ease of use. But when you combine FastAPI with uv
for dependency management and Docker Compose watch for live reloading, you get a development workflow that’s both fast and frictionless.
In this guide, we’ll set up a FastAPI project from scratch using uv
for dependency management, run it in Docker, and use compose watch
to sync changes instantly.
1. Why uv
?
uv
is a drop-in replacement for pip and virtualenv that manages Python packages and environments with blazing speed. It’s perfect for modern Python development, and it integrates cleanly into container builds, reducing image build times and making dependency management more reliable.
2. Getting Started
First, install uv with Homebrew; other instructions are on their installation page.
brew install uv
Now, let's set up the project directory and use python 3.12
uv init -p 3.12 python-fastapi-uv-example
This creates for us a python-uv-example directory and the following files:
.
├── main.py
├── pyproject.toml
└── README.md
1 directory, 3 files
Delete the main.py
. It's not needed for this app.
3. The FastAPI App
Now, let's create a simple FastAPI app that retrieves the server's IP address.
Create a new file: app/main.py
:
import requests
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
def get_ip_address():
try:
response = requests.get('https://api.ipify.org?format=json')
response.raise_for_status()
ip_data = response.json()
return ip_data.get('ip', 'IP address not found')
except requests.RequestException as e:
return f"Error occurred: {e}"
@app.get("/ip")
async def get_client_ip(request: Request):
client_host = request.client.host
return JSONResponse(content={"ip": get_ip_address()})
This app calls the ipify API to retrieve the public IP address of the server running the container.
4. Adding Dependencies with uv
For this, we'll need to add some dependencies:
uv add fastapi --extra standard
uv add requests
[project]
name = "python-fastapi-uv-example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastapi[standard]>=0.116.1",
]
To test out the app run:
uv run fastapi dev
Then curl
the endpoint:
curl localhost:8000/ip
5. Dockerfile with uv
Adding uv
from the astral repo.
# syntax=docker/dockerfile:1.4
FROM python:3.12-slim AS builder
# Install uv.
COPY --from=ghcr.io/astral-sh/uv:0.8 /uv /uvx /bin/
WORKDIR /app
COPY pyproject.toml uv.lock .
RUN uv sync --frozen
COPY app ./app
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app /app
EXPOSE 8000
# Run the application.
CMD ["/app/.venv/bin/fastapi", "run", "app/main.py", "--port", "8000", "--host", "0.0.0.0"]
Build the container
docker build -t python-fastapi-uv-example:latest .
And run it
docker run -p 8000:8000 python-fastapi-uv-example:latest
Again, test it with curl localhost:8000/ip
6. Docker Compose with Watch
Create docker-compose.yaml
:
services:
develop:
build: .
ports:
- "8000:8000"
develop:
watch:
- action: sync
path: .
target: /app
ignore:
- .venv/
- action: rebuild
path: ./pyproject.toml
The develop.watch
feature syncs local changes into the container instantly and rebuilds if pyproject.toml
changes.
7. Running the Project
Build the container and run watch.
docker compose build && docker compose up --watch
With Watch enabled, any changes to your code will instantly update in the running container. No manual restarts required.
Note: make sure you do a build each time you do a pull when the container is not running so that you have the latest code before running the watch.
8. Testing the API
Visit:
http://localhost:8000/ip
You’ll get back JSON with the server’s public IP address:
{"ip": "203.0.113.42"}
Next Steps
Now go ahead and add linting and formatting to make your development life-cycle easier with Python Linting.
Final Thoughts
By combining FastAPI with uv
and Docker Compose Watch, you get a rapid feedback loop without the usual friction of rebuilding containers for every code change. This setup is ready to be expanded into a full production workflow, while still keeping local development lightning fast.
I've added the repo for this article on github: https://github.com/markcallen/python-fastapi-uv-example