Flask + PostgreSQL expense tracking API with JWT authentication, user-scoped expense CRUD, and pytest test layers (unit, integration, end-to-end).
- User signup and login
- JWT-based authorization (
Authorization: Bearer <token>) - Expense CRUD scoped to the authenticated user
- Expense listing with date filters (
week,month,3months,custom) - Defensive input validation for malformed JSON, missing fields, and invalid query params
- Python 3.13+
- PostgreSQL
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt- Create
.envin project root:
SECRET_JWT=your-strong-secret- Update
config/database.iniwith your PostgreSQL credentials:
[postgresql]
host=localhost
dbname=mydb
user=your_user
password=your_password
port=5432python database/database.pyThis creates:
userscategoriesexpenses
flask --app app run --debugDefault local URL: http://127.0.0.1:5000
POST /signupPOST /login- Returns:
{"token": "<jwt>"}on success
- Returns:
JWT usage for protected routes:
Authorization: Bearer <jwt>GET /expenses- Query params:
filter=week|month|3months|customstart_date=YYYY-MM-DD(custom only)end_date=YYYY-MM-DD(custom only)limit=<positive integer>
- Query params:
POST /expenses- Required JSON fields:
cost,category_id - Optional JSON fields:
description
- Required JSON fields:
PUT /expenses/<expense_id>- Optional JSON fields:
cost,category_id,description - Omitted fields keep their existing DB values (partial update behavior)
- Optional JSON fields:
DELETE /expenses/<expense_id>
The API returns structured errors:
{"error": "..."}or field-level details:
{"error": {"field_name": "message"}}Common status codes:
400validation errors401authentication errors403authorization errors404not found409conflict (e.g., duplicate username/email)
Test structure:
tests/unit— service and utility logictests/integration— repository behavior and query constructiontests/e2e— API route behavior with Flask test client
Run all tests:
python -m pytest -qProject idea by Roadmap.sh