02 / 11
Full Stack
WealthDeck
A self-hosted personal finance command center for Pakistani investors.
- ROLE
- Full-stack developer, solo project
- TIMELINE
- 2026, ongoing
- BUILT FOR
- Personal use — tracking my own portfolio across every asset class I hold
- STACK
- Next.js 14 (App Router, client-only shell) · React 18 · TypeScript · Tailwind CSS · Radix UI · Framer Motion · Recharts · Node.js · Express · Prisma 5 · PostgreSQL (Supabase)
- PLATFORM
- Web app, publicly shareable with a safe read-only demo mode

THE PROBLEM
My money was spread across a dozen places with no common currency between them: PSX stocks in a brokerage app, crypto on an exchange, gold and silver in a spreadsheet, a provident fund statement from HR once a year, a voluntary pension scheme, an emergency fund, a few mutual fund goals, Zakat due once a year, and a monthly salary that had to be split between expenses and investing before any of that other tracking even started.
None of those tools talk to each other. None of them answer the one question that actually matters: what is everything worth, right now, and is it growing?
THE IDEA
Build one dashboard that owns the answer. Every asset class gets its own page with its own purchase/sale history, but they all roll up into a single net-worth number, computed server-side so the logic lives in one place instead of being recalculated differently on every screen.
Beyond the pages shown here, it also covers Gold & Silver, Mutual Funds, SPUS, a Provident Fund and VPS retirement dashboard, an Emergency Fund target, recurring Subscriptions with per-seat splitting, and a Schedule Planner for upcoming financial actions.
A tool only I would use daily has to be fast to open, trustworthy with real money, and honest about what's demo data versus what's mine.
WHAT IT DOES
01
Dashboard: net worth, in focus
One screen answers "how am I doing": total net worth against invested capital, today's and all-time change, a diversification score, a growth chart with configurable ranges, allocation by asset class, performance by category against a blended benchmark, and progress against a target allocation plan.

02
PSX Stocks
Pakistan Stock Exchange holdings with buy/sell history, dividends, a watchlist, and realized P/L preserved even after a position is fully closed.

03
Cryptocurrency
Purchases and sales with realized vs. unrealized profit split on an average-cost basis, a live price refresh, and USDT→PKR conversion tracked separately so FX movement doesn't get mistaken for coin performance.

04
Salary Allocation
A monthly worksheet that splits income into expenses and investments by percentage or fixed amount, carried forward month to month.

05
Ledger
Multiple named ledgers (e.g. a renovation) with events and line-item entries, so a big one-off spend doesn't get lost in a single flat list.

06
Zakat, done properly
Zakat across the whole portfolio against the silver Nisab threshold, with the Hijri calendar date it becomes due and a full breakdown of how the number was reached — not just a total.

HOW IT WORKS
Browser ──► Next.js (client-rendered shell, reads theme/privacy from localStorage)
│
└── /api/* rewritten to ──► Express API ──► Prisma ──► PostgreSQL (Supabase)
│
├── Price updaters (PSX, crypto, gold, silver, SPUS)
├── Intelligence service (net worth, diversification, trend)
└── node-cron: snapshots, scheduled portfolio emailsOne intelligence endpoint, not twelve reimplementations
Every page could have computed its own slice of "what's this worth," but net worth, diversification and day-change all live in one intelligence.service.ts so the dashboard and every detail page agree with each other by construction, not by convention.
Demo mode is a read-only mirror, not a toggle
Signed-out visitors get seeded demo data from the same endpoints the real data uses. A single-user login (env-configured credentials) unlocks the real portfolio; nothing about the UI branches on auth except the demo banner and the write permissions underneath it.
A computed ledger, not a running balance
Funds like the Provident Fund and Mutual Funds don't store a cached current value — they store transactions (contributions, NAV changes, a configurable profit rate) and compute the current value from that ledger every time. It's slower than a cached column, but it can never drift out of sync with its own history.
CHALLENGES AND WHAT I LEARNED
Prisma's dev migration flow fought the live database
The project runs against a live Supabase Postgres, and prisma migrate dev kept failing against it over the network. The fix was changing habit, not tooling: write the schema change, run prisma migrate diff to generate the SQL by hand, review it, then prisma migrate deploy. Slower, but every migration gets read now instead of trusting a shadow database that couldn't be fully controlled.
Categorization stayed boring on purpose
Early on there was a temptation to route transaction categorization through an LLM. It stayed rule-based instead — deterministic, free to run, and auditable. If a stock gets miscategorized, the rule can be grepped and fixed directly; an LLM call would have made that a guessing game, for a cost the project doesn't need to carry.
A serverless host doesn't like a PDF parser
The backend deploys as a Vercel function, and pulling in pdf-parse (which drags in pdfjs) crashed the function at boot because it expects a DOMMatrix that doesn't exist outside a browser. Lazy-loading the parser behind a stub, imported only when a PDF-handling route actually fires, keeps the rest of the API from paying for a dependency it doesn't need on every cold start.
Free tier, deliberately
Vercel ×2 (frontend and backend) and Supabase Postgres, all on free tiers. That constraint shaped real decisions — no LLM tier, no Redis, snapshots taken opportunistically on dashboard reads instead of a dedicated worker — and the app is faster for it, not slower.
RESULTS
Tracks 8 asset classes end to end: PSX stocks, crypto, gold, silver, mutual funds, SPUS, provident fund and VPS.
One dashboard replaces what used to be a brokerage app, an exchange, a spreadsheet, and a yearly HR statement.
Runs entirely on free-tier infrastructure, with no recurring cost.
Safe to share publicly: the live link is demo-data-only until sign-in.
STACK
LINKS

NEXT PROJECT
GitHub Defect Marker
05 / CONTACT