Electron Desktop App Architecture — Full Stack with AI
Architecture for an offline-first desktop application built with Electron, bundling a Next.js frontend, Express.js backend, and Python AI service into a single distributable app.
Tech Stack
| Layer |
Technology |
Purpose |
| Desktop Shell |
Electron |
Window management, IPC bridge, app:// protocol |
| Frontend |
Next.js (Pages Router) + TypeScript |
UI, routing, dual-path API client (IPC or fetch) |
| Styling |
Tailwind CSS + CSS variables |
Custom theme system with data-theme attribute |
| State |
React Context + Zustand + custom hooks |
Global state, complex UI state, server state |
| Backend |
Express.js + TypeScript |
REST API, file uploads (Multer), business logic |
| ORM / DB |
Prisma + SQLite |
Type-safe queries, local database |
| AI Service |
Python FastAPI + Uvicorn |
RAG engine, LLM orchestration, vector search |
| Local LLM |
Any GGUF model via llama-cpp-python |
Offline AI generation |
| Cloud LLMs |
OpenAI, Anthropic, Gemini, DeepSeek, Groq |
Online AI generation |
| Vectors |
ChromaDB + sentence-transformers |
Embeddings, similarity search |
| Licensing |
LemonSqueezy (via website middleman) |
License activation, validation, device management |
| Packaging |
electron-builder + PyInstaller |
Windows .exe / macOS .dmg |
System Architecture
- Website is the API middleman (holds the LemonSqueezy API key)
- Local SQLite stores activation state for offline use
- 14-day offline grace period after last successful validation
- Machine fingerprint prevents key sharing across devices
Dev vs Production
| Aspect |
Dev |
Production (packaged) |
| Frontend |
http://localhost:3000 |
app://./index.html (static export) |
| Backend |
ts-node-dev index.ts |
node backend/dist/index.js |
| AI Service |
.venv/Scripts/python -m api.main |
aiservice.exe (PyInstaller) |
| Database |
backend/database/app.db |
AppData/Roaming/AppName/data/app.db |
| Vectors |
aiservice/data/chroma/ |
AppData/Roaming/AppName/data/chroma/ |
| Uploads |
backend/uploads/ |
AppData/Roaming/AppName/uploads/ |
| Detection |
app.isPackaged = false |
app.isPackaged = true |
Build Commands
| Command |
What It Does |
npm run dev |
Backend + frontend in browser (localhost:3000) |
npm run desktop |
Compiles all + launches Electron window |
npm run pack |
Builds unpacked directory (for testing) |
npm run dist:win |
Builds Windows distributable |
npm run dist:mac |
Builds macOS distributable |
File Structure
desktop-app/
├── frontend/
│ ├── api/ # Dual-path API client (IPC + fetch)
│ ├── components/ # Shared reusable components
│ │ ├── ui/ # Atoms (Button, Modal, VirtualList, etc.)
│ │ ├── layout/ # Container, Header, Sidebar
│ │ ├── forms/ # Checkbox, ColorPicker, FormField, etc.
│ │ └── data-display/ # EmptyState, Grid, List
│ ├── contexts/ # React Context providers
│ ├── hooks/ # Custom hooks (per module)
│ ├── modules/ # Feature modules
│ ├── pages/ # Next.js Pages Router
│ ├── services/ # Business logic layer
│ ├── store/ # Zustand stores
│ ├── utils/ # Per-module utilities
│ ├── styles/ # Tailwind + CSS variable theme system
│ └── public/assets/ # Static assets (themes, images, fonts)
│
├── backend/
│ ├── index.ts # Express entry point
│ ├── paths.ts # Path utilities (dev vs packaged)
│ ├── routes/ # Route groups
│ ├── database/ # Service layers + Prisma client
│ ├── prisma/ # schema.prisma + seed.ts
│ └── uploads/ # File storage (fonts, music, images, themes)
│
├── aiservice/ # Python AI Service
│ ├── api/ # FastAPI server + routes
│ ├── services/ # AI service helpers
│ ├── prompts/ # Prompt templates
│ ├── models/ # Local LLM model (GGUF)
│ └── data/chroma/ # ChromaDB vectors (dev mode)
│
├── electron/ # Electron desktop wrapper
│ ├── main.ts # IPC handlers, service spawner, app:// protocol
│ └── preload.ts # Secure bridge (window.electronAPI)
│
├── electron-builder.yml # Packaging configuration
├── afterPack.js # Post-build script
└── package.json # Root scripts (dev, desktop, pack, dist)