Skip to main content

Architecture

Cashlytics uses a Next.js App Router architecture with a strong separation between pages, components, actions, and data access.

Tech Stack​

AreaTechnology
FrameworkNext.js 16.4 (App Router), React 19.3, TypeScript
DatabasePostgreSQL via Drizzle ORM 0.45 and postgres (postgres-js); SQL migrations in drizzle/
Authenticationnext-auth v5 (5.0.0-beta.32), Credentials provider, JWT sessions, Drizzle adapter
ValidationZod 4
i18nnext-intl 4 (German and English, messages in messages/)
AIVercel AI SDK (ai 7, @ai-sdk/openai 4), OpenAI models
Emailnodemailer with react-email templates (src/emails)
Pushweb-push (VAPID)
UITailwind CSS 4, Recharts 3

Authentication​

Sessions are JWT-based (auth.ts and auth.config.ts in the repository root). src/proxy.ts is the Next.js 16 proxy (the successor of middleware). It reads the session token with getToken and redirects anonymous visitors to /login.

The proxy lets these paths through without a session:

  • /api/auth (next-auth handlers)
  • /api/cron/ (secured by CRON_SECRET)
  • /api/v1/ (secured by personal API tokens, see REST API)
  • static assets, /sw.js, and /~offline
  • the public pages /login, /register, /forgot-password, and /reset-password

Route handlers and Server Actions do not rely on the proxy alone. They check the session themselves (requireAuth() in src/lib/auth/require-auth.ts), and update actions verify ownership (src/lib/auth/ownership.ts).

High-Level Structure​

  • auth.ts, auth.config.ts: next-auth setup
  • src/proxy.ts: session check and route exemptions
  • src/app/: route-level entry points ((auth), (dashboard), ~offline, session-expired) and API routes
  • src/actions/: server actions for reads and writes
  • src/components/: UI composition layers (layout, molecules, organisms, providers, pwa, settings, ui)
  • src/lib/: shared code
    • db/: Drizzle schema and client
    • ai/: tools, receipt scanning, CSV reconciliation
    • services/: calculations shared by Server Actions and the REST API
    • api/: helpers for REST API v1
    • auth/, validations/, import/, insights/, cron/, pwa/, email/
    • recurrence.ts, exchange-rates.ts, currency.ts, rate-limiter.ts
  • src/hooks/: reusable client behavior hooks
  • src/emails/: email templates
  • drizzle/: SQL migrations (0000 to 0022 as of v0.10.0)
  • messages/: translation files
  • public/sw.js: service worker
  • e2e/: end-to-end scripts

Data Model​

Tables are defined in src/lib/db/schema.ts:

  • Auth: users, auth_accounts, auth_sessions, auth_verification_tokens, password_reset_tokens, api_tokens
  • Finance: accounts, categories, expenses, incomes, daily_expenses, transfers, reserve_pots
  • Documents: documents (see Documents and Storage)
  • CSV import: import_sessions, import_rows, import_conflicts, import_decisions
  • AI: conversations, messages, merchant_categories, insights
  • Settings and infrastructure: user_settings, push_subscriptions, exchange_rates

All user-owned tables carry a userId. Typed entities are inferred from the Drizzle schema.

Design Patterns​

  • Component hierarchy of ui, molecules, organisms, and layout
  • Server Actions as the mutation boundary for the UI
  • Zod validation at data boundaries (src/lib/validations)
  • Shared calculation services (src/lib/services) used by both Server Actions and REST API v1

Data Flow​

  1. UI triggers an action or page-level fetch.
  2. Server Action checks the session, validates input, verifies ownership, and calls Drizzle ORM.
  3. Action returns a typed response payload.
  4. Relevant routes are revalidated after writes.

Other entry points:

  • Route Handlers under src/app/api for AI chat, receipt scanning, push subscriptions, documents, exchange rates, and cron jobs.
  • REST API v1 (/api/v1/*) with token authentication for external integrations.
  • A scheduler container in the Docker Compose setup that calls the cron endpoints over HTTP (see Scheduled Jobs).

For the route and action overview see API and Server Actions.