Architecture
Cashlytics uses a Next.js App Router architecture with a strong separation between pages, components, actions, and data access.
Tech Stack
| Area | Technology |
|---|---|
| Framework | Next.js 16.4 (App Router), React 19.3, TypeScript |
| Database | PostgreSQL via Drizzle ORM 0.45 and postgres (postgres-js); SQL migrations in drizzle/ |
| Authentication | next-auth v5 (5.0.0-beta.32), Credentials provider, JWT sessions, Drizzle adapter |
| Validation | Zod 4 |
| i18n | next-intl 4 (German and English, messages in messages/) |
| AI | Vercel AI SDK (ai 7, @ai-sdk/openai 4), OpenAI models |
nodemailer with react-email templates (src/emails) | |
| Push | web-push (VAPID) |
| UI | Tailwind 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 byCRON_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 setupsrc/proxy.ts: session check and route exemptionssrc/app/: route-level entry points ((auth),(dashboard),~offline,session-expired) and API routessrc/actions/: server actions for reads and writessrc/components/: UI composition layers (layout,molecules,organisms,providers,pwa,settings,ui)src/lib/: shared codedb/: Drizzle schema and clientai/: tools, receipt scanning, CSV reconciliationservices/: calculations shared by Server Actions and the REST APIapi/: helpers for REST API v1auth/,validations/,import/,insights/,cron/,pwa/,email/recurrence.ts,exchange-rates.ts,currency.ts,rate-limiter.ts
src/hooks/: reusable client behavior hookssrc/emails/: email templatesdrizzle/: SQL migrations (0000to0022as of v0.10.0)messages/: translation filespublic/sw.js: service workere2e/: 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, andlayout - 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
- UI triggers an action or page-level fetch.
- Server Action checks the session, validates input, verifies ownership, and calls Drizzle ORM.
- Action returns a typed response payload.
- Relevant routes are revalidated after writes.
Other entry points:
- Route Handlers under
src/app/apifor 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.