A production-ready REST API using FastAPI and PostgreSQL, deployed to Convox in minutes.
FastAPI is one of the fastest-growing Python web frameworks — type-safe, with automatic OpenAPI documentation out of the box, and one of the highest-performing Python options for building APIs. Paired with PostgreSQL for persistent storage, containerized with Docker, and deployed to Convox, this stack gives you a working API backend with managed infrastructure and zero Kubernetes configuration.
This example builds a task management API with full CRUD, input validation, health checks, and interactive Swagger docs. Use it as a starting point for internal tools, microservice backends, data-driven web apps, or any project where you need a Python API with a relational database.
Make sure you have a Convox account and a rack set up.
# Clone the repo
git clone https://github.com/convox-examples/fastapi-postgres.git
cd fastapi-postgres
# Create the app and deploy
convox apps create fastapi-postgres --wait
convox deploy --wait
# Verify it's running
convox servicesYou should see output like:
SERVICE DOMAIN PORTS
web web.fastapi-postgres.your-router.convox.cloud 443:8000
Open the domain in your browser to see the interactive Swagger docs at /docs, or test with curl:
curl https://web.fastapi-postgres.<your-router>.convox.cloud/health
# {"status": "healthy"}- FastAPI with type-safe request/response models and auto-generated OpenAPI docs at
/docs - PostgreSQL provisioned automatically — Convox creates and links the database with no manual setup
- SQLModel ORM combining SQLAlchemy's power with Pydantic's validation in a single model definition
- Health check that verifies database connectivity, used by Convox for automated restarts
- Connection pooling with
pool_pre_pingfor resilience across database restarts - Startup retry logic that handles the database still initializing on first deploy
- Input validation with length limits and type checking, returning clear error messages
The API provides full CRUD operations for a task resource. Every endpoint returns JSON.
curl -X POST https://<your-domain>/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Set up CI pipeline", "description": "Configure GitHub Actions for the new service"}'{
"title": "Set up CI pipeline",
"description": "Configure GitHub Actions for the new service",
"completed": false,
"id": 1,
"created_at": "2026-04-15T18:09:47.181492"
}curl https://<your-domain>/tasks[
{
"title": "Set up CI pipeline",
"description": "Configure GitHub Actions for the new service",
"completed": false,
"id": 1,
"created_at": "2026-04-15T18:09:47.181492"
}
]Supports pagination with ?offset=0&limit=20.
Partial updates — only send the fields you want to change:
curl -X PUT https://<your-domain>/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'{
"title": "Set up CI pipeline",
"description": "Configure GitHub Actions for the new service",
"completed": true,
"id": 1,
"created_at": "2026-04-15T18:09:47.181492"
}curl -X DELETE https://<your-domain>/tasks/1{"deleted": true}FastAPI validates all request data using Pydantic models. Missing or invalid fields return a 422 response with details:
curl -X POST https://<your-domain>/tasks \
-H "Content-Type: application/json" \
-d '{}'{
"detail": [
{
"type": "missing",
"loc": ["body", "title"],
"msg": "Field required"
}
]
}| Method | Path | Description |
|---|---|---|
| GET | / |
API info and available routes |
| GET | /health |
Health check (verifies DB connection) |
| GET | /docs |
Interactive Swagger UI documentation |
| POST | /tasks |
Create a task |
| GET | /tasks |
List tasks (supports offset and limit) |
| GET | /tasks/{id} |
Get a single task |
| PUT | /tasks/{id} |
Update a task (partial updates supported) |
| DELETE | /tasks/{id} |
Delete a task |
The convox.yml defines one service and one resource:
resources:
database:
type: postgres
services:
web:
build: .
port: 8000
health:
path: /health
grace: 15
resources:
- database
scale:
count: 1
memory: 512When you run convox deploy, Convox:
- Builds the Docker image from the Dockerfile
- Provisions a PostgreSQL database (if it doesn't already exist)
- Injects
DATABASE_URLinto the web service environment automatically - Starts the service and begins health checking
/health - Routes HTTPS traffic to port 8000 — TLS is handled for you
The application creates its database tables on startup using SQLModel's create_all(). No migration tool is needed for this example, but see Extending This Example for adding Alembic when your schema evolves.
Run the API locally with a PostgreSQL instance (via Docker or a local install):
# Start a local Postgres (if you don't have one running)
docker run -d --name pg -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:16
# Set up a virtual environment
python -m venv .venv
source .venv/bin/activate
# Set the connection string (see .env.example for reference)
export DATABASE_URL=postgresql://postgres:password@localhost:5432/postgres
# Install dependencies and run
pip install -r requirements.txt
uvicorn main:app --reload --port 8000If port 5432 is already in use, change -p 5432:5432 to -p 5433:5432 and update the port in DATABASE_URL accordingly.
The API is now running at http://localhost:8000. Open http://localhost:8000/docs for the interactive Swagger UI.
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Provided by Convox postgres resource |
Adjust the number of containers and memory allocation:
convox scale web --count 2 --memory 1024The default type: postgres resource runs a containerized PostgreSQL suitable for development and testing. For production workloads on AWS, use rds-postgres to provision a managed RDS instance:
resources:
database:
type: rds-postgres
options:
class: db.t3.medium
storage: 100
encrypted: true
durable: true
deletionProtection: true
backupRetentionPeriod: 7
services:
web:
build: .
port: 8000
health:
path: /health
grace: 15
resources:
- database
scale:
count: 2
memory: 512Key production options:
| Option | Description |
|---|---|
class |
RDS instance type (e.g., db.t3.medium, db.m5.large) |
storage |
Allocated storage in GB |
encrypted |
Enable storage encryption |
durable |
Multi-AZ deployment for high availability |
deletionProtection |
Prevent accidental database deletion |
backupRetentionPeriod |
Days to retain automated backups |
See the Convox PostgreSQL resource documentation for all available options.
This example is deliberately minimal. Here are common next steps when building on it:
- Database migrations — Add Alembic to manage schema changes. SQLModel's
create_all()works for initial setup, but Alembic tracks incremental migrations as your models evolve. - Authentication — FastAPI integrates with OAuth2 and JWT via its security utilities. Add token-based auth without changing the existing route structure.
- Background tasks — Use FastAPI's
BackgroundTasksfor quick async work, or add a Convox timer resource for scheduled jobs like cleanup or report generation. - Multiple services — Split into separate services in
convox.yml(e.g., an API service and a worker). Each gets its own scaling and resource allocation. - Custom domain — Point your domain at the Convox router and configure TLS. See the custom domains guide.
├── main.py # FastAPI application and route handlers
├── models.py # SQLModel database and API models
├── database.py # Database engine, session, and health check
├── requirements.txt # Python dependencies
├── Dockerfile # Container image definition
├── convox.yml # Convox deployment configuration
└── .env.example # Environment variable reference for local dev