A production-ready Task Management API built with FastAPI, SQLModel, and PostgreSQL (Neon).
- Full CRUD operations for tasks
- Async database operations with SQLModel
- Type-safe Pydantic schemas
- Comprehensive test suite with pytest
- Environment-based configuration
- Framework: FastAPI
- Database: PostgreSQL (Neon cloud-hosted)
- ORM: SQLModel
- Testing: pytest + httpx
- Async Driver: asyncpg
task-manager/
├── app/
│ ├── __init__.py
│ ├── config.py # Settings and environment variables
│ ├── database.py # Database engine and session setup
│ ├── models.py # SQLModel database models
│ ├── schemas.py # Pydantic request/response schemas
│ └── main.py # FastAPI application and routes
├── tests/
│ ├── __init__.py
│ └── test_tasks.py # CRUD endpoint tests
├── .env.example # Environment template
├── requirements.txt # Python dependencies
└── README.md # This file
- Python 3.10+
- PostgreSQL database (Neon cloud)
- pip or uv package manager
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# or
.\venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txtCopy the example environment file and update with your Neon credentials:
cp .env.example .envEdit .env with your Neon PostgreSQL credentials:
# Database (Neon PostgreSQL)
DATABASE_HOST=your-host.neon.tech
DATABASE_PORT=5432
DATABASE_NAME=neondb
DATABASE_USER=your-username
DATABASE_PASSWORD=your-passwordOr use a single DATABASE_URL:
DATABASE_URL=postgresql+asyncpg://user:password@host:5432/databaseSQLModel automatically creates tables based on your model definitions. The tables will be created when the application starts.
Task Table Schema:
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
task VARCHAR(255) NOT NULL,
time_estimate INTEGER NULL
);The SQLModel model definition (app/models.py):
from sqlmodel import SQLModel, Field
class Task(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
task: str = Field(max_length=255, nullable=False)
time_estimate: int | None = Field(default=None, nullable=True)You can initialize the database tables using one of these methods:
Option A: Run the application (auto-creates tables on startup)
uvicorn app.main:app --reloadOption B: Run the init script directly
# scripts/init_db.py
import asyncio
from app.database import engine
from app.models import SQLModel
async def init_db():
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
print("Database tables created successfully!")
if __name__ == "__main__":
asyncio.run(init_db())python scripts/init_db.py# Development with auto-reload
uvicorn app.main:app --reload
# Production
uvicorn app.main:app --host 0.0.0.0 --port 8000The API will be available at http://localhost:8000
Once running, access the interactive API docs:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
| Method | Endpoint | Description |
|---|---|---|
| GET | /tasks/ |
List all tasks |
| GET | /tasks/{task_id} |
Get a single task |
| POST | /tasks/ |
Create a new task |
| PUT | /tasks/{task_id} |
Update a task |
| DELETE | /tasks/{task_id} |
Delete a task |
Create a task:
curl -X POST "http://localhost:8000/tasks/" \
-H "Content-Type: application/json" \
-d '{"task": "Complete project report", "time_estimate": 120}'Response:
{
"id": 1,
"task": "Complete project report",
"time_estimate": 120
}List all tasks:
curl "http://localhost:8000/tasks/"Update a task:
curl -X PUT "http://localhost:8000/tasks/1" \
-H "Content-Type: application/json" \
-d '{"task": "Updated task name", "time_estimate": 60}'Delete a task:
curl -X DELETE "http://localhost:8000/tasks/1"The project uses pytest for testing with async support.
pytest -vpytest --cov=app --cov-report=term-missingpytest tests/test_tasks.py -vThe test suite uses:
- In-memory SQLite for fast testing (configured in
tests/conftest.py) - Fresh database session per test
- Automatic table creation before tests
All configuration is managed through environment variables via Pydantic Settings:
| Variable | Description | Default |
|---|---|---|
DATABASE_HOST |
PostgreSQL host | localhost |
DATABASE_PORT |
PostgreSQL port | 5432 |
DATABASE_NAME |
Database name | tasks_db |
DATABASE_USER |
Database username | postgres |
DATABASE_PASSWORD |
Database password | - |
DATABASE_URL |
Full connection URL (overrides individual settings) | - |
APP_HOST |
Application host | 0.0.0.0 |
APP_PORT |
Application port | 8000 |
APP_DEBUG |
Enable debug mode | false |
- Create a free account at Neon
- Create a new project
- Get your connection string from the dashboard
- Update your
.envfile with the credentials
Example Neon connection string:
postgresql+asyncpg://username:password@ep-xyz.us-east-1.aws.neon.tech/neondb?sslmode=require
The project follows Python and FastAPI best practices:
- Type hints for all functions
- Async/await for database operations
- Pydantic v2 for validation
- SQLModel for ORM
- Define the model in
app/models.py - Create schemas in
app/schemas.py - Add endpoints in
app/main.py - Write tests in
tests/test_tasks.py
MIT