gil lu
work

Sokin

Personal Finance Platform

Sokin preview

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.

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.

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.

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.

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.