A learning-focused backend project built to consolidate a modern Python API stack using FastAPI, PostgreSQL, SQLAlchemy 2.0, Alembic, and Docker.
The goal is not just to "make a CRUD work", but to build a small backend with a clean structure, incremental architecture, and predictable development workflow, while avoiding unnecessary complexity.
This project reinforces backend fundamentals through a complete but intentionally small full-stack setup.
It covers:
- FastAPI backend design
- PostgreSQL integration via Docker
- SQLAlchemy 2.0 ORM modeling
- Alembic migrations workflow
- Service-layer architecture (without overengineering)
- Vue frontend consumption of a real API
- Reverse proxy routing with Nginx
Main entity: Task
Fields:
- id
- title
- description (optional)
- is_done
- created_at
- updated_at
Tasks:
- POST /tasks
- GET /tasks
- GET /tasks/{id}
- PATCH /tasks/{id}
- DELETE /tasks/{id}
- Python
- FastAPI
- Uvicorn
- PostgreSQL
- SQLAlchemy 2.0
- Alembic
- Pydantic
- Vue 3
- Vite
- Nginx
- Docker
- Docker Compose
This project is intentionally designed to remain:
- simple
- explicit
- debuggable
- incremental
- close to real-world backend usage without enterprise complexity
What it is:
- a minimal full-stack CRUD system
- backend-first with frontend validation
- reverse-proxy integrated deployment setup
todo-fastapi/
- backend/
- frontend/
- nginx/
- alembic/
- docs/
- docker-compose.yml
- Makefile
- .env
- README.md
app/main.py
- FastAPI entry point
- router registration
- middleware (CORS)
app/core/
- config
- database setup
app/models/
- SQLAlchemy ORM models
app/schemas/
- Pydantic request/response schemas
app/services/
- CRUD logic
- filtering
- pagination
- business rules (minimal)
app/api/routes/
- HTTP layer
- dependency injection
- endpoint definitions
A minimal SPA that:
- lists tasks
- creates tasks
- updates tasks
- deletes tasks
- toggles completion
- supports filtering
No advanced architecture is used intentionally.
Nginx is the single entry point:
Routing rules:
- / → Vue SPA
- /api/* → FastAPI backend
Important rule:
SPA fallback must NEVER intercept /api routes.
.env
POSTGRES_USER=admin POSTGRES_PASSWORD=secret-password POSTGRES_DB=todo_db
DATABASE_URL=postgresql://admin:secret-password@db:5432/todo_db
Start everything:
make
Access:
- Frontend: http://localhost
- API: http://localhost/api/tasks
Core commands:
make up make down make
Migrations:
make migrations-create M="message" make migrations-up
DB utils:
make wait-db make test-db make seed
Debug:
make db-clean make query-tables make query-data
Maintenance:
make rm-pycache make clean make fclean
- make up
- develop backend/frontend
- make migrations-create
- make migrations-up
- iterate via /api
- Incremental design (no overengineering)
- Service layer only for CRUD isolation
- Nginx as reverse proxy + static host
- Frontend is intentionally minimal
/ and /tasks/ may behave differently depending on routing and proxy.
Location matching affects whether requests reach backend or SPA.
Vue fallback must not capture API routes.
Backend:
- CRUD complete
- PostgreSQL integrated
- migrations working
- service layer implemented
Frontend:
- Vue SPA working
- full API integration
Infrastructure:
- Docker Compose
- Nginx reverse proxy
- single entry point setup
This project demonstrates:
- real backend structure without overengineering
- full API lifecycle
- containerized workflow
- frontend/backend integration
- reverse proxy behavior in production-like setup