A lean, high-performance personal finance dashboard: a stateless Laravel 12 REST API, a React 19 SPA with zero runtime dependencies beyond React, and PostgreSQL 16 running in an isolated private schema on Supabase.
- Live Application: https://fintrack-ui-tan.vercel.app
- API Health Check: https://fintrack-api-08b9.onrender.com/api/health
- API Specification (OpenAPI 3.0):
contracts/openapi/fintrack-api.yaml
- What it does
- Product tour
- Architecture
- Tech stack & version rationale
- Data model & financial precision
- API & contracts
- Frontend design system
- Security & credential scanning
- CI/CD pipeline
- Cloud deployment & keep-awake
- Running locally
- Testing strategy
- Engineering decisions worth reading
- Known limits
- Documentation
FinTrack delivers real-time financial clarity without spreadsheet bloat or heavy frontend libraries:
| Flow | What the user experiences |
|---|---|
| Monthly overview | Select any month (YYYY-MM) and view instant calculations for total income, expenses, and net balance. |
| Transaction tracking | Fast entry of categorized income and expenses with validated decimal amounts and custom date assignment. |
| Category breakdown | Dynamic distribution chart and leading expense category insights computed directly by the database. |
| Custom calendar | Seamless month-picker and date selector with keyboard navigation and zero external UI packages. |
| Reactive updates | Instant UI synchronization without full page reloads. |
flowchart TB
subgraph Client["Client Tier · Vercel"]
FE["React 19 SPA<br/>Vite 7 · Vanilla CSS"]
end
subgraph Backend["Application Tier · Render"]
API["Laravel 12 REST API<br/>PHP 8.3 OPcache · Apache"]
end
subgraph Data["Data Tier · Supabase"]
PL["Session Pooler<br/>pgBouncer"]
DB[("PostgreSQL 16<br/>Schema: fintrack")]
PL --> DB
end
FE -->|"HTTPS / JSON<br/>/api/dashboard<br/>/api/transactions"| API
API -->|"PDO PostgreSQL<br/>SSL mode: require"| PL
The application is structured as a decoupled full-stack monorepo:
- Frontend (Vercel): Static React SPA served via global Edge CDN.
- Backend API (Render): Containerized PHP 8.3 / Laravel 12 web service.
- Database (Supabase): Managed PostgreSQL 16 instance accessed through transactional poolers.
| Layer | Technology | Version | Rationale |
|---|---|---|---|
| Runtime / Language | PHP | 8.3 |
Strict typing, typed class constants, performance optimizations with OPcache. |
| Backend Framework | Laravel | 12.0 |
Lean REST routing, robust FormRequest validation, and native Eloquent decimal casting. |
| Frontend UI | React | 19.1 |
Concurrent rendering, declarative state management, zero runtime UI dependencies. |
| Frontend Tooling | Vite | 7.0 |
Ultra-fast HMR and optimized tree-shaking for minimal bundle footprint. |
| Database | PostgreSQL | 16.0 |
ACID transactional reliability, DECIMAL(12,2) precision, and composite indexes. |
| Testing | PHPUnit / Vitest | 11.5 / 3.2 |
Feature/Unit test suites in backend and isolated helper testing in frontend. |
| API Contracts | OpenAPI / Redocly | 3.0.3 |
Standardized REST contract with CI linting. |
| Containerization | Docker | Multi-stage | Hardened Apache production image (php:8.3-apache) with automated entrypoint migrations. |
| CI / CD | GitHub Actions | Workflows | Parallel testing, full-history secret scanning, contract linting, and Docker validation. |
Transactional data is stored in PostgreSQL inside the isolated fintrack schema:
CREATE SCHEMA IF NOT EXISTS fintrack AUTHORIZATION postgres;
REVOKE ALL ON SCHEMA fintrack FROM public;
CREATE TABLE fintrack.transactions (
id BIGSERIAL PRIMARY KEY,
type VARCHAR(10) NOT NULL,
category VARCHAR(50) NOT NULL,
description VARCHAR(255) NULL,
amount DECIMAL(12, 2) NOT NULL,
occurred_on DATE NOT NULL,
created_at TIMESTAMP(0) WITHOUT TIME ZONE NULL,
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NULL
);- No Floating Point Noise: Amounts use
DECIMAL(12,2)in PostgreSQL and'amount' => 'decimal:2'in Laravel. - Composite Index
(occurred_on, type): Monthly dashboard queries (SUM(amount)grouped by category for a specific month) scan index leaf pages directly rather than performing full table scans. - Schema Isolation: Storing tables in
fintrackinstead ofpublicprevents automatic exposure by Supabase's PostgREST data API.
Full data modelling details: docs/02-modelagem-dados.md.
The API follows strict REST conventions and is formally specified in contracts/openapi/fintrack-api.yaml:
| Endpoint | Method | Purpose | Response |
|---|---|---|---|
/api/health |
GET |
Container liveness and uptime probe | 200 OK |
/api/dashboard |
GET |
Monthly totals (income, expense, balance, category breakdown) | 200 OK / 422 |
/api/transactions |
GET |
List transactions (ordered by date desc, optional ?month=YYYY-MM) |
200 OK |
/api/transactions |
POST |
Create a new transaction | 201 Created / 422 |
/api/transactions/{id} |
GET |
Retrieve single transaction | 200 OK / 404 |
/api/transactions/{id} |
PUT |
Update existing transaction | 200 OK / 422 / 404 |
/api/transactions/{id} |
DELETE |
Remove transaction record | 204 No Content / 404 |
Full API contract guide: docs/03-api-contratos.md.
The user interface uses CSS Custom Properties (tokens) defined in frontend/src/styles.css:
- No CSS framework dependencies (no Tailwind, Bootstrap, or component libraries).
- Custom Charts: Ratio bars and category distributions rendered via native CSS and SVG.
- Microscopic Bundle: Total production build bundle footprint is under 60 KB (gzipped).
Full frontend architecture: docs/04-frontend.md.
FinTrack enforces a multi-layered credential security barrier:
- Pre-commit Hook (
.githooks/pre-commit):- Inspects staged files (
git diff --cached) for credential formats (Stripe keys, GitHub tokens, AWS keys, database URIs with passwords, private keys) before allowing a commit.
- Inspects staged files (
- History-Wide Secret Scanning (
.github/scripts/scan-secrets.sh):- Runs in GitHub Actions CI with
fetch-depth: 0, scanning all commits across repository history (git rev-list --all) to ensure no forgotten keys remain accessible in past revisions.
- Runs in GitHub Actions CI with
The GitHub Actions pipeline (.github/workflows/ci.yml) executes on every push and pull request with cancel-in-progress: true:
flowchart LR
subgraph CI["GitHub Actions CI Pipeline"]
BE["backend<br/>PHP 8.3 + Postgres 16<br/>PHPUnit Tests & Coverage"]
FE["frontend<br/>Node 22<br/>Vitest & Vite Build"]
SEC["secrets<br/>Full Git History Scan<br/>scan-secrets.sh"]
CT["contracts<br/>OpenAPI Linting<br/>Redocly CLI"]
IMG["images<br/>Docker Buildx<br/>Backend & All-In-One"]
BE --> IMG
FE --> IMG
CT --> IMG
end
| Service | Provider | Configuration |
|---|---|---|
| Frontend | Vercel | Edge Network, automatic deployment from main. |
| Backend API | Render | Docker runtime, defined via render.yaml. |
| PostgreSQL | Supabase | Managed PostgreSQL 16 (AWS us-west-2 pooler, SSL require). |
Render free-tier instances hibernate after 15 minutes of inactivity. To prevent cold-start delays during working hours without exhausting monthly quotas, .github/workflows/keep-awake.yml executes a lightweight health check ping every 10 minutes between 9:00 and 16:00 BRT on weekdays (~154h/month, safely within the 750h limit).
# Start PostgreSQL and production-like app container
docker compose up -d
# Or start with live hot-reloading frontend development server
docker compose --profile dev up -d- App URL: http://localhost:8000
- Vite Dev Server: http://localhost:5173
- PostgreSQL:
localhost:5432(fintrack/ password from.env)
Backend:
cd backend
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan serveFrontend:
cd frontend
npm install
npm run dev| Suite | Technology | Focus |
|---|---|---|
| Backend Feature Tests | PHPUnit 11 | API endpoints (/health, /dashboard, /transactions), validation errors, database integrity. |
| Backend Unit Tests | PHPUnit 11 | Eloquent model casting, fillable rules, decimal formatting. |
| Frontend Unit Tests | Vitest 3 | Currency formatting (formatCurrency) and date parsing utilities. |
Run tests locally:
# Backend test suite
cd backend && php artisan test
# Frontend test suite
cd frontend && npm test- Database Aggregations vs Memory Loops: Sums and groupings are calculated directly in SQL (
SUM(amount)withGROUP BY category). Querying aggregated totals from indexed columns keeps memory usage constant regardless of transaction volume. - Zero Runtime UI Dependencies: Eliminating component libraries (Tailwind, MUI, Chart.js) produces sub-60 KB production bundles and guarantees complete control over accessibility and CSS layout.
- Schema Isolation in Managed PostgreSQL: Creating a dedicated
fintrackschema prevents Supabase from exposing transactional data via public Data APIs. - Development Reverse Proxy: Vite's dev server proxies
/apirequests to the Laravel backend container, matching production behavior and avoiding CORS preflight requests locally.
- Single-User Scope: Current version operates as a single-tenant instance (multi-user authentication is scheduled for Phase 3).
- No Export Options: CSV and PDF export endpoints are planned in Phase 5.
- No Direct Bank Sync: Intentionally excluded to avoid third-party banking aggregator dependencies and maintain an educational, transparent architecture.
Roadmap details: docs/05-roadmap-produto.md.
- Architecture & Design Decisions — System boundaries, Docker multi-stage build, and security.
- Data Modelling & Indexing — Relational schema, composite indexes, and decimal precision.
- API Contracts & Endpoints — REST API specifications and validation rules.
- Frontend Architecture — CSS design tokens, custom SVG charts, and state management.
- Product Roadmap — Completed milestones and upcoming feature phases.
- API Specification (OpenAPI 3.0) — Formal machine-readable contract.



