-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
NexTask is a single desktop application that internally behaves like a small full-stack app. This page explains the moving parts and how a request flows through them.
NexTask runs three logical layers inside one Electron application:
flowchart LR
subgraph Electron["Electron App"]
subgraph Renderer["Renderer process (Chromium)"]
UI["Vue 3 UI<br/>PrimeVue · Tailwind"]
Stores["Pinia stores<br/>(cache + actions)"]
Axios["Axios (api.helper)"]
end
subgraph Main["Main process (Node.js)"]
Fastify["Fastify REST API<br/>:3000"]
Prisma["Prisma Client<br/>(better-sqlite3 adapter)"]
Store["electron-store<br/>(settings)"]
end
DB[("SQLite<br/>dev.db / app.db")]
end
UI --> Stores --> Axios
Axios -- "HTTP :3000" --> Fastify
Fastify --> Prisma --> DB
UI -- "IPC (settings:get/set)" --> Store
Key idea: the Fastify server does not run in a separate process — it is started inside the Electron main process (app.whenReady). The renderer talks to it over plain HTTP on localhost:3000, exactly as if it were a remote backend. This keeps the frontend/backend boundary clean and makes the REST API reusable.
The Node.js side. On startup (src/main/main.ts) it:
- Creates the
BrowserWindow(context isolation on, node integration off, apreloadscript bridging the two worlds). - Sets a Content-Security-Policy header (
script-src 'self'). - Runs database migrations then seeds.
- Starts the Fastify API.
- Registers IPC handlers for the settings store (
settings:get,settings:set) and a debugmessagechannel.
It owns everything that needs Node.js / filesystem / native modules: the HTTP API, Prisma, SQLite, and the persistent settings store.
The Chromium side — a standard Vue 3 single-page app. It has no direct database or filesystem access. It reaches data in two ways:
- REST over HTTP for tasks and stages (via Axios → Fastify).
-
IPC for user settings (via the
preloadcontextBridge).
Because context isolation is enabled, the renderer cannot touch Node APIs directly. The preload script uses contextBridge to expose a tiny, safe surface on window:
window.electronAPI = { sendMessage(msg) } // debug logging channel
window.settings = { get(key), set(key, value) } // wraps ipcRenderer.invoke('settings:get' | 'settings:set')This is the only channel between the UI and Node.
sequenceDiagram
participant U as User
participant V as Vue component<br/>(TaskDialog)
participant P as Pinia store<br/>(useTaskStore)
participant A as Axios (api.helper)
participant F as Fastify route<br/>(POST /tasks)
participant DB as Prisma → SQLite
U->>V: Fill form, click "Save"
V->>P: taskStore.saveTask(newTask)
P->>A: api.post('/tasks', newTask)
A->>F: HTTP POST :3000/tasks
F->>DB: prisma.task.create({ data })
DB-->>F: created Task
F-->>A: 200 + Task JSON
A-->>P: Task
P->>P: update local cache (entities + allEntities)
P-->>V: created Task
V->>V: emit "task-saved" → board updates
The Pinia store is the client-side gateway: components never call Axios directly. Every store action performs the HTTP call and keeps a local cache in sync, so the UI can render optimistically without re-fetching. See Frontend for the caching strategy.
Two core entities plus a bookkeeping table:
erDiagram
STAGE ||--o{ TASK : contains
STAGE {
int id PK
string name
int position
}
TASK {
int id PK
string title
string description
string version
int position
bool isHistorized
datetime historizationDate
int stageId FK
}
SEED {
int id PK
string name UK
bool executed
datetime createdAt
}
- A Stage is a Kanban column; a Task is a card. A task belongs to at most one stage (
stageIdis nullable). -
Archiving a task sets
isHistorized = true, stampshistorizationDate, and clearsstageId(removing it from the board). - Deleting a stage sets its tasks'
stageIdtoNULLat the DB level (ON DELETE SET NULL); the app additionally archives those tasks first. - The
Seedtable (mapped to_seeds) tracks which seed scripts have run.
Full details in Database.
| Layer | Technology |
|---|---|
| UI framework | Vue 3 + PrimeVue + TailwindCSS |
| State management | Pinia |
| Routing | Vue Router (hash history) |
| HTTP client | Axios |
| Desktop shell | Electron |
| API server | Fastify (+ Swagger) |
| ORM / DB | Prisma + SQLite (better-sqlite3 adapter) |
| Settings persistence | electron-store |
| Build tooling | Vite, TypeScript, electron-builder |
See Tech Stack for the reasoning behind each choice.
-
A real REST API (rather than direct DB calls from the renderer) keeps a clean separation and doubles as a documented, testable interface (Swagger UI at
/docs). - Prisma in the main process keeps native modules and filesystem access out of the sandboxed renderer.
- Pinia caching makes the UI feel instant while still using the API as the source of truth.
NexTask — a modern cross-platform desktop todo app · Electron · Vue 3 · Prisma · TailwindCSS Repository · Licensed under Apache-2.0
Getting Started
Understanding the App
Deep Dives
Workflow