CASE STUDY · 01
Q-Balance
Income and expense tracking with per-user data and reports.
- Next.js 16
- React 19
- TypeScript
- Server Actions
- MariaDB
- Better Auth
- Tailwind CSS
No sign-up needed: browse six months of sample data, or use the one-click demo account to add and edit records.

Overview
Q-Balance tracks income and expenses. Transactions are built from reusable items, items belong to categories, and the dashboard compares daily income with spending over a week, a month, or six months.
The aim was a small but realistic line-of-business app: authentication with email verification, data that belongs to a user, CRUD screens, filters, and reports. These are the same building blocks as a customer portal or an internal dashboard.
Engineering decisions
- 01
User ownership in every query
Each read and write is scoped to its owner in SQL, and the schema enforces it again.
Every query for categories, items, and transactions filters by the owner's ID, using parameterized SQL through mysql2 rather than an ORM. Reads go through a helper that resolves the data owner (the signed-in user, or the read-only showcase account for guests), while writes require a real signed-in user. Composite foreign keys in the schema guarantee that a transaction's item, and an item's category, belong to the same user, so a bug in application code cannot link one user's data to another's.
- 02
Two demo modes, zero friction
Guests browse a read-only showcase; the demo account gets its own resettable copy.
Visitors can explore immediately without an account. Guests read a password-less showcase account that nobody can sign in to or modify. The one-click demo account has its own copy of the sample data, so demo edits never leak into what guests see. Both are regenerated once a day on first use, without a cron job, and each calendar date produces the same deterministic data, so the history moves forward a day at a time instead of being reshuffled.
- 03
Server Actions for mutations
Forms post to Server Actions with pending states, revalidation, and tested calculations.
Creating and editing records uses Server Actions with useActionState for validation messages and pending feedback, followed by path revalidation. Money is stored as DECIMAL(15, 2) and quantities as DECIMAL(15, 4). The discount calculation and password rules are covered by tests that run with the Node.js test runner.
Architecture
- Rendering
- Server Components load data directly on the server. Client Components handle forms, filters, navigation, and the Recharts chart.
- Mutations
- Server Actions validate input, check the writer, run parameterized SQL, and revalidate affected paths.
- Data
- MariaDB through a shared mysql2 connection pool. Seven tables with foreign keys, unique constraints, and query indexes.
- Auth
- Better Auth with email/password, required email verification, password reset, and session revocation on password changes.
What I would build next
- CSV import and export of transactions.
- Recurring transactions and monthly budgets per category.
- End-to-end tests for the main flows with Playwright.