FastAPI + PostgreSQL todo application with JWT auth, hierarchical tasks (parent/subtasks), and a static HTML frontend.
- User registration and login
- JWT-protected todo APIs
- Nested todos using
parent_id - Soft-finish flow (task is marked
completed=trueinstead of hard delete) - Finished parent-task cleanup endpoint
- Browser frontend at
frontend/index.html
- Python (
pyproject.tomlrequires>=3.13) - FastAPI
- SQLAlchemy
- Pydantic v2
- PostgreSQL +
psycopg2-binary - Alembic
python-jose(JWT)passlib[argon2](password hashing)- Uvicorn
Docker uses python:3.13-slim to match the >=3.13 project requirement.
app/main.py- FastAPI app setup, CORS, router registrationapp/api/auth.py- auth endpointsapp/api/todos.py- todo endpointsapp/services/- business logicapp/models/- SQLAlchemy modelsapp/schemas/- request/response schemasfrontend/index.html- static UIalembic/- migrationsdocker-compose.yml- app + database services
Set these in .env:
DATABASE_URLSECRET_KEYACCESS_TOKEN_EXPIRE_MINUTES(default30if omitted)
Example DATABASE_URL:
DATABASE_URL=Your_postgres_URL_here- Install dependencies:
uv sync- Start PostgreSQL (for example with Docker):
docker compose up -d db- Apply migrations:
uv run alembic upgrade head- Start API:
uv run uvicorn app.main:app --reload- Open UI:
- Open
frontend/index.htmlin browser - API base URL used by UI:
http://127.0.0.1:8000
docker compose up --buildStartup behavior:
dbruns PostgreSQL 18appwaits for DB health, runsalembic upgrade head, then starts Uvicorn- app container uses DB host
db(notlocalhost)
Services:
- API:
http://127.0.0.1:8000 - PostgreSQL:
localhost:5432(postgres/toor, DB:todoapp)
Workflow file: .github/workflows/ci-cd.yml
- CI (
testjob):- Runs on every push and pull request
- Uses Python 3.13
- Installs dependencies from
requirements.txt - Runs
pytestwith CI-safe environment values
- CD (
dockerjob):- Runs only on push to
mainafter tests pass - Builds Docker image from
Dockerfile - Pushes image to GitHub Container Registry:
ghcr.io/<owner>/tier1-smart-todo:latestghcr.io/<owner>/tier1-smart-todo:sha-<commit>
- Runs only on push to
For package publishing, ensure Actions has permission to write packages in your GitHub repository settings.
All /todos routes require:
Authorization: Bearer <token>POST /auth/register- Body:
{ "email": "...", "password": "..." } - Creates a user with hashed password
- Body:
POST /auth/login- Body:
{ "email": "...", "password": "..." } - Returns:
{ "access_token": "..." }
- Body:
POST /todos/- Create todo
- Body:
title(required)description(optional)deadline(optional datetime)parent_id(optional UUID of existing user-owned parent task)
GET /todos/- Returns user todos as a nested tree using
subtasks
- Returns user todos as a nested tree using
PUT /todos/{todo_id}- Partial update fields:
titledescriptioncompleted
- Partial update fields:
DELETE /todos/{todo_id}- Soft-finish behavior: marks todo as completed and returns:
{ "finished": true, "id": "<todo_id>", "completed": true }
DELETE /todos/finished/clear-parents- Hard-deletes completed root tasks (
parent_id IS NULL) - Returns
{ "cleared": <count> }
- Hard-deletes completed root tasks (
id(UUID, PK)email(unique, indexed)hashed_password
id(UUID, PK)titledescription(nullable)completed(boolean, defaultfalse)deadline(nullable datetime)created_at(datetime)user_id(FK ->users.id)parent_id(nullable self-reference FK ->todos.id,ondelete="CASCADE")
frontend/index.html supports:
- register/login/logout
- create root todos and subtasks
- edit todo title/description/completed
- mark task finished
- view finished tasks
- clear finished parent tasks
Token is stored in browser localStorage.
- CORS is fully open (
allow_origins=["*"], all methods and headers). Base.metadata.create_all(...)is currently disabled inapp/main.py; schema should be managed through Alembic.- Some migrations are placeholders (
pass), so verify migration history for fresh environments.