Skip to main content
back to blog

building czmoney: from spreadsheet pain to personal finance pwa

building czmoney: from spreadsheet pain to personal finance pwa

back in my first year of college, i started tracking every rupiah i spent in google sheets. simple, accessible, cross-platform—it worked. until it didn't.

the spreadsheet struggle

the setup was straightforward: one column for the month, rows for income, more rows for expenses, and a total at the bottom. worked great for january through march. but by december? i was scrolling endlessly, hunting for the right cell, losing track of where i was.

i tried a horizontal layout—six months across, then six more below. better, but not by much. june and december still meant awkward scrolling and squinting at tiny cells on my phone. the breaking point came mid-month when i checked my balance and realized i was almost broke. again. the spreadsheet wasn't helping me stay aware; it was just recording my mistakes.

that's when the idea hit: why not build an app? i had the time, the curiosity, and a real problem to solve. czmoney was born from that frustration.

starting simple, thinking big

the first version was bare-bones: authentication, basic crud operations, and a pwa setup so it could work offline. nothing fancy. but as i used it daily, i kept noticing friction points. syncing across devices was clunky. i wanted insights, not just data. i needed it to work seamlessly whether i had internet or not.

so i kept iterating. over 600+ commits, czmoney evolved from a simple tracker into a full-stack progressive web app with features i genuinely needed:

  • offline-first architecture with indexeddb—transactions save locally first, sync when online
  • ai-powered insights using google gemini 2.5 flash—monthly summaries, spending patterns, budget alerts
  • real-time sync via supabase realtime—changes reflect instantly across devices
  • smart categorization—auto-categorizes expenses based on description patterns
  • comprehensive security—rate limiting with upstash redis, input validation, xss protection

system architecture

the architecture follows a layered approach with clear separation between client state, application logic, and external services. the client layer handles ui rendering and offline persistence, while the application layer manages synchronization and real-time updates. api routes provide a security boundary with authentication and rate limiting before reaching external services.

offline-first data flow

when users create transactions offline, the system immediately saves to indexeddb and updates the ui optimistically. once connectivity returns, the sync service processes the queue with idempotency guarantees to prevent duplicates. supabase realtime broadcasts changes across all connected clients, ensuring consistency without manual refreshes.

the technical journey

building czmoney taught me more than any tutorial ever could. here are the key challenges i tackled:

1. offline-first is harder than it sounds

making an app work offline isn't just about caching. it's about handling conflicts, managing sync queues, and ensuring data consistency. i implemented an optimistic ui pattern where changes appear instantly, then sync in the background. if something fails, it rolls back gracefully.

// Simplified sync queue logic
const syncQueue = {
  pending: [],
  async process() {
    while (this.pending.length > 0) {
      const transaction = this.pending[0];
      try {
        await api.sync(transaction);
        this.pending.shift(); // Remove on success
      } catch (error) {
        if (error.status === 409) {
          // Handle conflict
          await this.resolveConflict(transaction);
        } else {
          break; // Retry later
        }
      }
    }
  }
};

2. ai integration without breaking the bank

i wanted ai-powered insights, but the project budget was tight. then i discovered google's student program—free gemini 2.5 flash for a year. perfect timing. the api was fast, the streaming responses made the ux feel snappy, and i didn't have to worry about costs piling up. i added rate limiting (3 requests per minute for ai endpoints) to keep things reasonable, but honestly, the free tier was more than enough for my needs.

3. security from day one

as a personal project, it's tempting to skip security. i treated the app as a real deployment and implemented:

  • multi-tier rate limiting (10 req/10s standard, 3 req/min for ai)
  • input validation with zod schemas
  • xss protection using dompurify
  • row-level security in supabase
  • csrf protection and secure headers

4. performance obsession

i didn't want czmoney to feel like a throwaway portfolio project, so i focused on perceived performance:

  • skeleton loaders instead of spinners
  • debounced search (300ms delay)
  • lazy-loaded components
  • prefetch navigation on hover
  • optimistic ui updates

the result? the app feels instant, even on slow connections. users see their changes immediately, and syncing happens invisibly in the background.

lessons learned

start with a real problem

czmoney wasn't built to pad my resume. it solved my actual problem. that kept me motivated through repeated iterations and debugging sessions. when you're solving your own pain point, you naturally build something useful.

iterate based on usage

the best features came from using the app daily. the ai insights? added after manually analyzing my spending for the third time. the smart categorization? born from typing "makan" for the hundredth time. real usage reveals real needs.

deployment discipline means thinking ahead

i could've skipped rate limiting, proper error handling, and security measures. but building those parts from the start taught me how deployed systems actually work. it's the difference between a portfolio piece and something you'd actually deploy.

documentation matters

i wrote comprehensive readme documentation not just for others, but for future me. six months later, i can still understand why i made certain architectural decisions. good docs are a gift to your future self.

the stack

here's what powers czmoney:

  • frontend: sveltekit 5, typescript, tailwindcss 4, apexcharts
  • backend: supabase (postgresql), restful api with auth middleware
  • ai: google gemini 2.5 flash with streaming responses
  • caching: upstash redis for rate limiting
  • deployment: vercel
  • offline: indexeddb, service workers, background sync

what's next

czmoney is live at czmoney.vercel.app. i use it every day, and it's saved me from more "wait, where did my money go?" moments than i can count.

but i'm not done. upcoming features include budget forecasting, recurring transaction support, and multi-currency handling. the codebase is on github if you want to dive deeper.

final thoughts

building czmoney taught me that the best projects come from scratching your own itch. it's not about using the trendiest tech or building something massive. it's about solving a real problem well, iterating based on feedback (even if that feedback is just you using it), and caring enough to handle the operational details that make an app usable after deployment.

from a frustrated spreadsheet user to shipping a full-stack pwa—that's the journey. and honestly? i'm just getting started.