Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

38 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Finapple 🍎

A full-stack personal finance platform with AI-powered document analysis, budget tracking, savings goals, and real-time spending analytics.


The Problem It Solves

Most people struggle with three things when managing personal finances:

  1. No visibility β€” they don't know where their money is going month to month
  2. Unread documents β€” bank statements, invoices, and contracts pile up unanalyzed
  3. No accountability β€” setting a budget mentally never sticks without a system

Finapple solves all three. It gives you a live wallet with categorized spending, a budget manager that tracks itself from your transactions, and an AI that reads your financial documents so you don't have to.


Live Demo

Service URL
Frontend https://finapple.netlify.app
Backend API https://finapple-0517.onrender.com

Tech Stack

Backend

Technology Purpose
NestJS Modular Node.js framework for the REST API
Drizzle ORM Type-safe SQL query builder
PostgreSQL (Neon) Serverless cloud database
Cloudinary File upload and storage (PDFs, images)
Mistral AI AI document analysis (two models)
JWT + bcrypt Authentication and password security
Passport.js JWT strategy middleware

Frontend

Technology Purpose
React 19 UI framework
TypeScript Type safety across the entire codebase
Zustand Lightweight global state management
Recharts Data visualization (area, bar, pie charts)
Tailwind CSS v4 Utility-first styling
shadcn/ui Component library (sidebar, toasts)
Axios HTTP client with interceptors
React Router v7 Client-side routing with protected routes
Vite Build tool

Features

πŸ” Authentication

  • Register and login with email + password
  • Passwords hashed with bcrypt (10 salt rounds)
  • JWT tokens stored in localStorage via Zustand persist
  • Auto token attachment via Axios request interceptor
  • Auto logout + redirect on 401 response
  • Profile name update and password change from Settings

πŸ’³ Wallet

  • Deposit money into your account
  • Withdraw with expense category tagging (Food, Rent, Transport, etc.)
  • Transfer money to other users by email
  • Full transaction history with running balance after each transaction
  • Category badges on each transaction row
  • Total money-in / money-out summary cards

πŸ“Š Budget Manager

  • Set monthly spending limits per expense category
  • Budget spending is tracked automatically β€” when you make a withdrawal with category "Food", the Food budget progress updates in real time
  • Visual progress bars per category:
    • 🟒 Green β€” on track (< 80%)
    • 🟑 Yellow β€” near limit (β‰₯ 80%)
    • πŸ”΄ Red β€” over budget (β‰₯ 100%)
  • Summary cards: total budgeted, total spent, remaining
  • Month picker to view past months
  • Auto-refresh on window focus + manual refresh button

🎯 Savings Goals

  • Create goals with a name, target amount, and optional deadline
  • Fund goals incrementally β€” add any amount at any time
  • Progress bar shows percentage complete
  • Auto-marks goal as completed when target is reached πŸŽ‰
  • Separate sections for active and completed goals

πŸ“ˆ Analytics

  • Daily Cash Flow β€” area chart showing income vs expense by day
  • Spending by Category β€” pie chart with color-coded segments
  • Category Breakdown β€” sorted list with percentage bars
  • 6-Month Overview β€” grouped bar chart (income, expense, savings per month)
  • Month picker to explore any past period
  • All charts built with Recharts

πŸ—„οΈ Vault

  • Upload PDFs and images securely to Cloudinary
  • View all uploaded files with type badges
  • Delete files (removes from both DB and Cloudinary)
  • Each file can be sent to AI for analysis

πŸ€– AI Document Analysis

The most distinctive feature. Built on Mistral AI with two different models depending on file type.

How it works

User clicks "Analyze" on a Vault file
        ↓
Backend checks if result is cached in DB
        ↓ (cache miss)
File type check:
  image β†’ pixtral-12b-2409 (vision model)
  PDF   β†’ mistral-small-latest (text model)
        ↓
Structured JSON extracted from document
        ↓
Result saved to analysis_results table
        ↓
Returned to frontend (future calls use cache)

Two models, one reason

File Type Model Why
Images (receipts, scans) pixtral-12b-2409 Multimodal vision β€” actually sees the image
PDFs, other docs mistral-small-latest Text-based, faster and more cost-efficient

What the AI extracts

{
  "documentType": "Invoice",
  "summary": "Invoice from ABC Corp for β‚Ή45,000 due on 15 June 2025...",
  "entities": ["ABC Corp", "HDFC Bank", "ACC-001234"],
  "dates": ["15 June 2025", "1 May 2025"],
  "amounts": ["β‚Ή45,000", "β‚Ή4,050 GST"],
  "keyTerms": ["Net 30", "Late payment penalty 2%"],
  "riskFlags": ["Overdue by 10 days"],
  "confidence": 0.91
}

Smart caching

Results are persisted in PostgreSQL. If the same file is analyzed again, the cached result is returned instantly β€” Mistral is never called twice for the same document. This saves API cost and eliminates wait time on repeat views.

Prompt engineering

A custom system prompt enforces strict JSON output with no markdown or extra text. The response parser strips any code blocks if the model wraps the output anyway, then falls back to raw text if JSON parsing fails entirely β€” so the feature never crashes.


Project Structure

Finapple/
β”œβ”€β”€ finabackend/                  # NestJS backend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ auth/                 # Register, login, JWT, profile
β”‚   β”‚   β”œβ”€β”€ wallet/               # Deposit, withdraw, transfer, stats
β”‚   β”‚   β”œβ”€β”€ budget/               # Monthly budget limits + spending calc
β”‚   β”‚   β”œβ”€β”€ savings/              # Savings goals CRUD
β”‚   β”‚   β”œβ”€β”€ vault/                # File upload/delete via Cloudinary
β”‚   β”‚   β”œβ”€β”€ analysis/             # Mistral AI document analysis
β”‚   β”‚   β”œβ”€β”€ cloudinary/           # Cloudinary provider + service
β”‚   β”‚   β”œβ”€β”€ db/                   # Drizzle DB module + schema
β”‚   β”‚   └── stratgies/            # JWT Passport strategy
β”‚   └── drizzle/                  # SQL migration files
β”‚
└── frontend/                     # React + Vite frontend
    └── src/
        β”œβ”€β”€ pages/
        β”‚   β”œβ”€β”€ Dashboard.tsx     # Overview with live stats
        β”‚   β”œβ”€β”€ Wallet.tsx        # Wallet with deposit/withdraw/transfer
        β”‚   β”œβ”€β”€ Analytics.tsx     # Charts and spending insights
        β”‚   β”œβ”€β”€ Budget.tsx        # Budget manager
        β”‚   β”œβ”€β”€ Savings.tsx       # Savings goals
        β”‚   β”œβ”€β”€ Vault.tsx         # File manager + AI analysis
        β”‚   └── Settings.tsx      # Profile + password
        β”œβ”€β”€ Store/
        β”‚   β”œβ”€β”€ authStore.ts      # Auth state (Zustand + persist)
        β”‚   β”œβ”€β”€ walletStore.ts    # Balance + transactions
        β”‚   β”œβ”€β”€ budgetStore.ts    # Budgets (refreshes after withdrawals)
        β”‚   β”œβ”€β”€ savingsStore.ts   # Goals state
        β”‚   β”œβ”€β”€ analyticsStore.ts # Monthly stats + trend
        β”‚   └── FilesDataStore.ts # Vault files
        β”œβ”€β”€ constants/
        β”‚   └── categories.ts     # Single source of truth for all categories
        └── api/
            └── api.ts            # Axios instance + interceptors

Database Schema

users            β†’ id, fullName, email, password, balance
transactions     β†’ id, userId, type, amount, balanceAfter, category, description
files            β†’ id, userId, publicId, url, resource_type
analysis_results β†’ id, fileId, userId, documentType, summary, entities...
budgets          β†’ id, userId, category, limitAmount, month
savings_goals    β†’ id, userId, name, targetAmount, savedAmount, deadline

Key Design Decisions

Why Drizzle over Prisma? Drizzle is lighter, has no code generation step, and gives full SQL control while keeping TypeScript type safety. Better fit for a project that needs fine-grained date range queries.

Why Zustand over Redux? Zero boilerplate, no Provider wrapping, and direct getState() access between stores. The wallet store calls budgetStore.getState().fetchBudgets() after a withdrawal β€” this cross-store communication is trivial in Zustand.

Why two AI models? A vision model on a PDF is wasteful β€” it adds latency and cost for no gain. pixtral-12b is used only when the file is actually an image. PDFs get mistral-small which is 5x faster.

Why cache AI results in the DB? Mistral calls can take 3-8 seconds. Showing a cached result instantly on re-visit is a much better UX, and it keeps API costs under control.

Category system as a shared constant Both Wallet withdrawals and Budget Manager use EXPENSE_CATEGORIES from a single src/constants/categories.ts file. This is the only way to guarantee they always match β€” the budget spending calculation on the backend matches exactly because the same string values are sent.


Environment Variables

Backend (.env)

DATABASE_URL=postgresql://...
JWT_SECRET=your_secret
CLOUDINARY_CLOUD_NAME=...
CLOUDINARY_API_KEY=...
CLOUDINARY_API_SECRET=...
MISTRAL_API_KEY=...
CORS_ORIGIN=https://your-frontend.netlify.app

Frontend (Netlify env vars)

VITE_API_URL=https://your-backend.onrender.com

Running Locally

# Backend
cd Finapple/finabackend
npm install
npm run start:dev

# Frontend
cd Finapple/frontend
npm install
npm run dev

Deployment

Service Platform Config
Backend Render render.yaml in repo root
Frontend Netlify netlify.toml in frontend folder

Render settings:

  • Root Directory: Finapple/finabackend
  • Build: npm install && npm run build
  • Start: npm run start:prod

Built with NestJS Β· Drizzle Β· Neon Β· Cloudinary Β· Mistral AI Β· React Β· Zustand Β· Recharts

About

Finnaple - a fintech platform where you feel more secure than home

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages