English | Español
A REST API for managing tasks and users across projects within groups — with JWT authentication, role-based permissions, per-project real-time chat, task comments, and async notifications. Built as a personal portfolio project focused on scalable backend design, collaborative systems, and real-time communication.
- Status: Active development / stable MVP
- Deploy: Local VM (Docker Compose + PostgreSQL)
Hierarchy: groups → projects → tasks → comments. Users register, join groups, get roles, and collaborate on projects in real time.
- Register, login, logout, and token refresh with JWT (HS256)
- Access token (default 15 min) + refresh token (default 7 days)
- Logout revokes sessions via DB-backed session rows (allowlist)
- Roles and permissions scoped per group and per project
- All secrets come from environment variables
- Create, edit, and delete groups
- Manage group members
- Create and administer projects inside groups
- Create, edit, delete, and assign tasks
- Comments on assigned tasks
- Status tracking and due-date control
- In-app notifications over WebSockets (task assigned, task updated, comment mention, …)
- Per-project chat (WebSocket + Redis pub/sub, channels
user:{id}andproject:{id}) - Pending notifications persisted asynchronously with Celery (Redis broker)
Modular layered architecture — each layer has one job, which keeps business logic testable in isolation and decouples the API from infrastructure:
flowchart LR
Client["Client (Web / REST)"] -->|"HTTP + JWT"| API["FastAPI routers<br/>auth · user · group · project · task · comment"]
Client <-->|"WebSocket"| WS["WS router /ws"]
API --> Services["Services (business logic)"]
Services --> Repos["Repositories"]
Repos --> PG[("PostgreSQL")]
WS --> SM["Socket manager"]
SM <-->|"pub/sub"| Redis[("Redis")]
SM -->|"push events"| Client
Celery["Celery worker"] <-->|"broker / results"| Redis
Celery --> PG
Sched["Scheduler (5h)"] --> PG
Notification flow — live users get an immediate push; offline users get a pending notification stored for later retrieval:
flowchart LR
Ev["Event (assigned · updated · mention)"] --> Redis[("Redis pub/sub")]
Redis --> SM["Socket manager"]
SM -->|"push if online"| Client["WebSocket client"]
SM -->|"else enqueue"| Celery["Celery worker"]
Celery -->|"persist pending"| PG[("PostgreSQL")]
- Routers (
api/v1/routers/): HTTP + WebSocket endpoint definitions - Dependencies (
dependency/): per-endpoint DI wiring (auth, permissions, sessions) - Services (
services/): business logic - Repositories (
repositories/): persistence, SQLModel over PostgreSQL - Models (
models/): DB models, request/response schemas, exceptions - Real-time layer (
core/socket_manager.py,core/event_ws.py): connection registry + Redis pub/sub fan-out - Background jobs: Celery worker (
tasks.py) for pending notifications; APScheduler (lifespan) runs a session-cleanup job every 5h (core/auto.py)
| Category | Technology |
|---|---|
| Language | Python 3.12 |
| Web framework | FastAPI |
| ORM / validation | SQLModel (Pydantic) |
| Database | PostgreSQL (+ Alembic migrations) |
| Cache / pub-sub | Redis |
| Async tasks | Celery (Redis broker) |
| Scheduler | APScheduler |
| Auth | JWT (python-jose, HS256) |
| Tests | pytest, pytest-cov |
| CI | GitHub Actions |
| Containers | Docker & Docker Compose |
- Python 3.12
- PostgreSQL and Redis (or just Docker + Docker Compose)
git clone git@github.com:SrStamm/API-de-tareas-con-FastAPI.git
cd API-de-tareas-con-FastAPIpython -m venv env
source env/bin/activate # Linux / macOS
env\Scripts\activate # Windows
# App dependencies
pip install -r requirements.txt
# Test dependencies (optional, needed to run the suite)
pip install -r requirements-test.txtCreate a .env file (see Environment variables), then:
uvicorn main:app --reloadThe API will be available at http://localhost:8000.
docker-compose up --buildThe API will be available at http://localhost:8000 (PostgreSQL and Redis included in the stack).
Create a .env file at the project root. SECRET_KEY and ALGORITHM are required — the app fails at startup without them. If DATABASE_URL is set, it takes precedence over the individual POSTGRES_* values.
| Variable | Used in | Default | Notes |
|---|---|---|---|
POSTGRES_USER |
db/database.py |
— | Required |
POSTGRES_PASSWORD |
db/database.py |
— | Required |
POSTGRES_DB |
db/database.py |
— | Required |
POSTGRES_HOST |
db/database.py |
— | Required |
POSTGRES_PORT |
db/database.py |
— | Required |
DATABASE_URL |
db/database.py |
— | Full SQLAlchemy URL; takes precedence over POSTGRES_* |
REDIS_HOST |
db/database.py, tasks.py |
localhost |
Redis host |
REDIS_PORT |
db/database.py, tasks.py |
6379 |
Redis port |
REDIS_DB |
db/database.py, tasks.py |
0 |
Redis database index |
REDIS_URL |
db/database.py |
— | When set, overrides REDIS_HOST/REDIS_PORT/REDIS_DB for the Redis client (parsed into host/port/db) |
REDIS_PASSWORD |
Docker setup only | — | Declared by the compose files for the Redis image. tasks.py reads it but the broker URL does not include it (unused) |
SECRET_KEY |
auth | — | Required — startup fails without it |
ALGORITHM |
auth | — | Required — e.g. HS256 |
ACCESS_TOKEN_DURATION |
services/auth_services.py |
15 |
Access token lifetime in minutes |
REFRESH_TOKEN_DURATION |
services/auth_services.py |
7 |
Refresh token lifetime in days |
Copy-pasteable example (fake values only — generate your own):
# PostgreSQL
POSTGRES_USER=postgres
POSTGRES_PASSWORD=change-me
POSTGRES_DB=tasks_db
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
# DATABASE_URL takes precedence when set:
# DATABASE_URL=postgresql+psycopg2://postgres:change-me@localhost:5432/tasks_db
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# REDIS_URL takes precedence for the Redis client when set:
# REDIS_URL=redis://localhost:6379/0
REDIS_PASSWORD=change-me
# Auth (required — startup fails without these)
SECRET_KEY=replace-with-a-long-random-string
ALGORITHM=HS256
ACCESS_TOKEN_DURATION=15
REFRESH_TOKEN_DURATION=7Once the server is running:
- Swagger UI: http://localhost:8000/docs
- Redoc: http://localhost:8000/redoc
| Command | What it does |
|---|---|
pytest -q --disable-warnings |
Run the full suite (needs Redis on localhost:6379) |
pytest -vv |
Verbose run |
pytest --cov=./api/v1 --cov-report=html |
Coverage of routers → htmlcov/index.html |
pytest --cov=. --cov-report=html |
Full-project coverage → htmlcov/index.html |
src/ci-test.shis a local convenience wrapper only (run manually). CI does not invoke it: the GitHub Actions workflow runspytestdirectly.
Notes:
- Tests run against SQLite (
test/conftest.py) with Redis atlocalhost:6379. src/test.shstarts Redis for you (docker compose up -d redis) before running pytest.- Requirements are split:
requirements.txt(app) vsrequirements-test.txt(pytest, pytest-cov, …).
CI: GitHub Actions (.github/workflows/test.yaml) runs on every push / PR to main: pytest over ./test/test_routers, plus a non-blocking flake8 lint pass.
- Frontend: https://front-task-api-vanilla.vercel.app/dashboard/tasks
- Username:
test - Password:
test
These credentials are for the public demo frontend only. The frontend is a demo client; the main focus of this project is the backend design and architecture.
Honest list of what is not there yet — each entry includes why it matters:
- Notification channels — real-time delivery is WebSocket + in-app only; no email or mobile push. Password reset and email verification are not implemented.
- Single-instance deployment — the demo runs on a single local VM; horizontal scaling and multi-instance WebSocket fan-out have not been load-tested.
- Test/database drift — the automated suite runs on SQLite while dev/staging use PostgreSQL; Postgres-specific queries are not exercised in CI, so a small behavioral drift risk remains.
- Rate limiting currently disabled — per-endpoint limits exist (
core/limiter.py) but the middleware wiring is commented out inmain.py; it must be re-enabled and its "limit exceeded" response fixed to HTTP 429 before public exposure. - Observability — structured logging (structlog) exists, but there is no
/healthendpoint, metrics, or tracing yet. - Coverage is reported but not gated — no minimum coverage threshold is enforced in CI, and WebSocket integration paths are only lightly tested (mocked unit tests, no end-to-end pub/sub test).
This project is licensed under the MIT License (OSI reference).