
Overview
Sokin puts an entire financial life in one dashboard: daily expenses, monthly budgets, savings goals, net worth, bill reminders, subscriptions, and a live stock portfolio. The frontend is a Next.js 16 / React 19 dashboard; behind it sits a Firebase-authenticated Express API backed by Firestore.
It goes beyond a CRUD tracker in three ways. Receipts are scanned with Google Cloud Vision OCR and auto-parsed into categorized expenses with confidence scoring. Market data flows through a Finnhub integration wrapped in a two-tier cache tuned per data type — 30-second quotes, 1-hour company profiles. And the API is engineered serverless-first: fail-closed distributed rate limiting, timing-safe cron authentication, and write-behind cache persistence designed for stateless function instances.
The system is substantial: 69 endpoints across 11 routers, 279 Jest tests across 15 suites, and 13 Firestore collections with per-user ownership enforced in both security rules and controllers.
The Problem
Personal finance data lives everywhere — bank apps, spreadsheets, a brokerage, receipts in a pocket. Most trackers solve the easy part (typing in expenses) and fall over on the real-world messiness: paper receipts, market-data rate limits, and the statelessness of serverless hosting.
Sokin treats those as engineering problems: an OCR pipeline turns receipts into structured expenses, a cached proxy makes free-tier market data feel live, and the rate limiter keeps enforcing even when its backing store is unreachable.

Dashboard — the signed-in home.
- Four KPI cards computed from live API data: monthly expense total with month-over-month change, 6-month average, current net worth, and upcoming bills.
- A Spending Trends composed chart — daily bars under a 30-day moving-average line — beside a category donut with per-category totals.
- This one page pulls from five backend endpoints in parallel.
Key Features
- Expense ledger — searchable, category-filterable, sortable, cursor-paginated with per-filter cache keys
- Receipt scanning — upload → sharp preprocessing → Cloud Vision OCR → merchant/amount/date extraction with confidence scoring → keyword auto-categorization
- Budgets, savings goals, bill reminders, and subscriptions with Firebase Cloud Messaging notifications
- Net worth — typed assets and liabilities with monthly snapshot documents powering a trends view
- Stock tracking — watchlists, trending/gainers/losers, and a portfolio computed from a buy/sell transaction ledger
- Two-tier cache (in-memory + Upstash Redis) with stale-while-revalidate background refresh
- Fail-closed rate limiting — write-behind counters, an in-memory degraded mode when Redis is unreachable, automatic recovery probes
- PWA service worker with stale-while-revalidate API caching; Sentry monitoring
Stack
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS
- Express
- Firebase
- Firestore
- Upstash Redis
- Google Cloud Vision
- Finnhub API
- Recharts
- Vercel
Architecture
The frontend is Next.js 16 App Router with React 19: a shadcn/ui-style component system on Radix primitives, Recharts for visualization, TanStack React Query plus contexts for state, react-hook-form with zod for forms, and framer-motion, GSAP, and three.js for motion and the landing visual.
The API is Express structured as routes → controllers → Joi validation schemas, with dedicated middleware for Firebase auth, validation, rate limiting, and errors. Every request carries a Firebase ID token verified server-side with firebase-admin — fail-closed. Both apps deploy to Vercel serverless, the Express API as a single function behind a catch-all rewrite; backend code branches on the serverless environment, so there are no timers and cache flushing is request-based.
Caching and limits are built for statelessness: the two-tier cache reads memory first and Upstash Redis for cross-instance sharing; rate-limit counters persist by write-behind batching (flush every 10 seconds, or every 5th request in serverless), and if Redis is unreachable the limiter fails closed into an in-memory degraded mode with recovery probes — it never stops enforcing. The Finnhub proxy uses per-endpoint cache keys and batched requests (5 concurrent, 100ms gaps) with per-symbol graceful degradation to stay inside free-tier limits.
Security is layered: per-user ownership on all 13 Firestore collections enforced in rules and controllers, receipt images in Firebase Storage, and cron endpoints locked behind a shared secret compared with SHA-256 and timingSafeEqual, optional IP allowlisting, and a strict attempt limiter.

Expenses — the core ledger.
- Searchable, category-filterable, sortable table with category chips and inline edit and delete.
- Backed by cursor-paginated API queries with per-filter cache keys.
- Entries originate from manual entry or the OCR receipt scanner.
Skills Applied
- Full-Stack Engineering
- Next.js 16 / React 19 dashboard against a 69-endpoint Express API — typed end to end, with a Radix-based component system and Recharts visualizations.
- Distributed Systems
- Two-tier caching with stale-while-revalidate, write-behind counter persistence, and fail-closed rate limiting designed for stateless serverless instances.
- Integrations
- Cloud Vision OCR receipt pipeline with confidence-scored extraction, a batched rate-aware Finnhub proxy, and FCM push notifications.
- Security
- Fail-closed Firebase token verification, per-user ownership across 13 Firestore collections in rules and controllers, and timing-safe cron authentication.
- Testing & Reliability
- 279 Jest tests across 15 suites, Sentry monitoring, and a PWA service worker with offline-friendly API caching.

Net worth — assets and liabilities as typed entities.
- Bank accounts, investments, and vehicles against credit cards, student loans, and auto loans — each category summed.
- Headline cards for total assets, total liabilities, and computed net worth with month-over-month change.
- Monthly snapshot documents drive the numbers and power a trends view.

Stocks — a cached, rate-limited market screen.
- Trending table with price, day change, 52-week range, and watchlist stars; tabs for search, watchlist, most active, and gainers/losers.
- The portfolio rail computes total value and gain/loss from a buy/sell transaction ledger aggregated into per-holding average cost.
- Every quote on screen came through the cached, rate-limited backend proxy — the browser never touches Finnhub directly.
Live Demo
A hosted demo is in the works — it will live right here on this page.