A full-stack expense tracking application with a FastAPI backend and a React + Vite frontend. The app lets you create, view, filter, sort, summarize, and delete personal expenses using a lightweight JSON-file data store.
- Add expenses with title, amount, category, and date
- View all expenses in a responsive frontend table
- Filter expenses by category, amount range, and date range
- Sort expenses by amount, date, or title
- Paginated expense API responses
- Expense summary with totals, averages, min/max values, and category breakdown
- Delete expenses
- FastAPI Swagger docs
- Pytest test coverage for routes, services, repositories, storage, config, middleware, and exceptions
Backend
- Python 3.12
- FastAPI
- Pydantic
- Uvicorn
- Pytest
- Ruff
Frontend
- React
- TypeScript
- Vite
- Axios
- Recharts
- Lucide React
- Oxlint
.
├── frontend/ # React + Vite frontend
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── services/ # API client
│ │ └── types/ # TypeScript types
│ └── package.json
├── src/ # FastAPI backend
│ ├── routes/ # API route handlers
│ ├── services/ # Business logic
│ ├── repositories/ # Data access layer
│ ├── storage/ # JSON storage helpers
│ ├── schemas/ # Request/response schemas
│ ├── models/ # Domain models
│ ├── exceptions/ # Custom errors and handlers
│ └── main.py # FastAPI app entrypoint
├── tests/ # Backend tests
├── requirements.txt # Python dependencies
└── README.md
- Python 3.12+
- Node.js 20+
- npm
From the project root:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtStart the FastAPI server:
uvicorn src.main:app --reload --host 0.0.0.0 --port 8001The API will be available at:
- API root:
http://localhost:8001 - Swagger docs:
http://localhost:8001/docs - ReDoc:
http://localhost:8001/redoc - Health check:
http://localhost:8001/health
Open a second terminal:
cd frontend
npm installCreate or update frontend/.env:
VITE_API_BASE_URL=http://localhost:8001/api/v1Start the frontend:
npm run devThe frontend will usually run at http://localhost:5173.
Base URL:
http://localhost:8001/api/v1
| Method | Endpoint | Description |
|---|---|---|
GET |
/expenses |
Get expenses with optional filtering, sorting, and pagination |
POST |
/expenses |
Create a new expense |
GET |
/expenses/{expense_id} |
Get one expense by ID |
DELETE |
/expenses/{expense_id} |
Delete one expense by ID |
GET |
/expenses/summary/ |
Get expense summary statistics |
curl -X POST http://localhost:8001/api/v1/expenses \
-H "Content-Type: application/json" \
-d '{
"title": "Groceries",
"amount": 56.75,
"category": "Food",
"date": "2026-08-01",
"description": "Weekly grocery shopping"
}'curl "http://localhost:8001/api/v1/expenses?category=Food&sort_by=date&order=desc&page=1&page_size=10"Run backend tests:
pytestRun backend linting:
ruff check .Run frontend linting:
cd frontend
npm run lintBuild the frontend:
cd frontend
npm run buildBackend settings can be configured with environment variables or a .env file.
| Variable | Default | Description |
|---|---|---|
APP_NAME |
Expense Tracker API |
FastAPI application name |
VERSION |
1.0.0 |
API version |
API_PREFIX |
/api/v1 |
API route prefix |
EXPENSES_FILE |
data/expenses.json |
Expense JSON storage path |
LOG_FILE |
logs/app.log |
Application log file path |
LOG_LEVEL |
INFO |
Logging level |
HOST |
0.0.0.0 |
Server host |
PORT |
8001 |
Server port |
ENVIRONMENT |
development |
Runtime environment |
Frontend configuration:
| Variable | Default | Description |
|---|---|---|
VITE_API_BASE_URL |
http://localhost:8001/api/v1 |
Backend API base URL |
For local development, keep VITE_API_BASE_URL set to http://localhost:8001/api/v1 so it matches the backend command above.
- Runtime data is stored in
src/data/expenses.json. - Local environment files, virtual environments, frontend dependencies, build output, caches, and temporary test folders are ignored by Git.
- This project is intended for learning and local development. For production, replace JSON-file storage with a database and add authentication.