This project is a fully containerized Python FastAPI backend connected to a robust PostgreSQL database. It utilizes Docker Compose to orchestrate the entire environment, ensuring that the API, the database, and the persistent storage volumes can be spun up simultaneously from a completely clean state.
You can run this entire stack with zero manual database configuration.
-
Set up your environment variables: Copy the provided template to create your local environment file:
cp .env.example .env -
Start the stack: Boot up the API and Database with a single command:
docker compose up
Note: The API is configured to wait for the database's health check to pass before starting.
All required variables are documented in .env.example. When you duplicate it to .env, the default Docker Compose database connection string will work automatically for local development.
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Returns basic API information and version |
GET |
/health |
Returns the health status of the API |
GET |
/tasks |
Retrieves all tasks (Supports query parameters: search, done, sort) |
GET |
/tasks/{task_id} |
Retrieves a specific task by its ID |
POST |
/tasks |
Creates a new task |
PUT |
/tasks/{task_id} |
Updates an existing task's title or completion status |
DELETE |
/tasks/{task_id} |
Deletes a task by its ID |
GET |
/stats |
Retrieves the total count of all tasks in the database |
Once the stack is running, you can test the connection and view the seeded data using the following command:
curl -i http://127.0.0.1:8000/tasksBelow is a screenshot verifying the active PostgreSQL tables and the seeded data, confirming the successful connection between the containerized API and the database

Docker containers are designed to be temporary, meaning any internal data is permanently destroyed when the container is removed. Volumes exist to solve this by storing data safely on the host machine's physical disk, allowing information to persist across complete system restarts.
A load balancer continuously pings this /health endpoint and automatically stops routing user traffic to this specific container if it reports a database connection failure.
I added an index on the done column and ran EXPLAIN ANALYZE before and after. Interestingly, PostgreSQL still chose a Seq Scan over an Index Scan. Because the seeded table only contains 3 rows, the database's query planner correctly calculated that reading the table sequentially is faster than loading the index into memory.
I added a Redis container to my compose.yaml network to prepare for future caching needs. I verified the container's health by executing a docker compose exec redis redis-cli PING command, confirming it is actively running alongside the database.
Implemented a multi-stage Dockerfile, which reduced the final production image size from 82.6 MB to 61.2 MB