Electron + Vite + React + Hono + Bun
packages/app/— Electron shell + React frontend (pages, components, styles)packages/app/electron/— Electron main process and preloadpackages/api/— Hono API server on Bun (localhost:3001), database schema and migrations
# Install dependencies
bun install
# Copy env and fill in secrets
cp .env.example .env
# Generate and apply database migrations
cd packages/api
bun run db:generate
bun run db:migrate
cd ../..
# Start dev (API + Electron app)
bun run devAll env vars live in a single .env file at the project root.
| Prefix | Available to | How to access |
|---|---|---|
VITE_ |
Desktop app (renderer) and API server | App: import.meta.env.VITE_* · API: process.env.VITE_* |
| No prefix | API server only | process.env.* |
Secrets (auth keys, DB credentials) must not have the VITE_ prefix — Vite strips non-prefixed vars at build time, keeping them out of the renderer.
Uses Drizzle ORM with SQLite via bun:sqlite.
cd packages/api
bun run db:generate # Generate migrations from schema
bun run db:migrate # Apply migrations
bun run db:studio # Browse data in Drizzle StudioSchema is in packages/api/src/database/schema.ts, migrations in packages/api/drizzle/.
Backend uses Hono running on Bun. The app consumes it via typed RPC client (hono/client).
// packages/api/src/index.ts
const app = new Hono()
.get("/ping", (c) => c.json({ message: "pong" }));
export type AppType = typeof app;// packages/app/src/lib/api.ts
const res = await api.ping.$get();
const data = await res.json();bun run build:mac # macOS (dmg + zip, x64 + arm64)
bun run build:win # Windows (nsis, x64)
bun run build:linux # Linux (AppImage + deb, x64)
bun run build:all # All platformsIMPORTANT: Don't assume how a package works from memory. Check the installed version in package.json and read docs in node_modules/<pkg>/ before using any package. APIs change between major versions — guessing leads to broken code.
This is a monorepo with two packages. packages/api runs on Bun (server-side), packages/app runs in Electron/Vite (client-side). Don't import server-only code (like bun:sqlite or process.env secrets) into the app package — use the RPC client or VITE_ env vars instead.