Electron Multi-Process Architecture
Zequel uses Electron’s multi-process architecture to separate concerns and improve security:- Main Process
- Renderer Process
- Preload Script
Node.js backend with full system access:
- Database connections (PostgreSQL, MySQL, etc.)
- File system operations (import/export)
- Native OS APIs (keychain, dialogs)
- IPC handler registration
- Window management
- Auto-updater
src/main/Process Communication Flow
Core Subsystems
1. Connection Management
Main Process (src/main/services/connections.ts):
- CRUD operations for saved connections
- Credential storage via OS keychain (keytar)
- Connection pooling and session tracking
- SSH tunnel management
- Auto-reconnection on connection loss
src/renderer/stores/useConnectionsStore.ts):
- Connection list state
- Active connection tracking
- Connection form validation
- User fills connection form in renderer
- Renderer calls
window.api.connections.save(config) - Preload invokes
connection:saveIPC handler - Main process saves to app database (SQLite)
- Password stored in OS keychain
- Result returned to renderer
2. Database Drivers
Architecture: Abstract base class + database-specific implementationssrc/main/db/postgres.ts- Usespg+knexsrc/main/db/mysql.ts- Usesmysql2+knexsrc/main/db/sqlite.ts- Usesbetter-sqlite3+knexsrc/main/db/duckdb.ts- Uses@duckdb/node-api+ custom Knex dialectsrc/main/db/mongodb.ts- Uses nativemongodbdriver (no Knex)src/main/db/redis.ts- Usesioredissrc/main/db/clickhouse.ts- Uses@clickhouse/clientsrc/main/db/sqlserver.ts- Usesmssql+knex
src/main/db/manager.ts):
- Singleton instance:
connectionManager - Maps session IDs → driver instances
- Handles connection pooling
- Automatic cleanup on disconnect
3. IPC Communication
Pattern: Request/response viaipcRenderer.invoke() and ipcMain.handle()
Example: Executing a query
All IPC channels are registered in
src/main/ipc/index.ts:
4. Data Streaming
Problem: Loading 1M+ rows blocks the UI and exceeds memory limits. Solution: Cursor-based streaming Architecture:- Main Process: Cursors in
src/main/db/cursors/BaseCursor- Abstract cursor interfacePostgresCursor,MySQLCursor, etc. - Database-specific cursorsCursorManager- Tracks active cursors by ID
- Renderer: Virtual scrolling (TanStack Virtual)
- Loads data in chunks (default 1000 rows)
- Requests next chunk when scrolling near bottom
- Renderer requests
stream:tableStart(connectionId, table, chunkSize) - Main creates cursor, returns
cursorId - Renderer requests
stream:read(cursorId)for next chunk - Main fetches rows via cursor, returns
{ rows, hasMore } - Repeat until
hasMore = false - Renderer calls
stream:cancel(cursorId)to cleanup
5. State Management (Pinia)
Stores use Composition API pattern:useConnectionsStore- Connection stateuseTabsStore- Query tab managementuseSettingsStore- App settings (theme, editor config)useSchemaStore- Cached schema metadata
6. Query Editor
Monaco Editor integration:- Language: SQL with dialect-specific syntax highlighting
- Auto-complete: Table/column suggestions from schema
- Multi-tab: Multiple query tabs per connection
- Parameterized queries: Placeholder support (
?,$1, etc.) - EXPLAIN visualization: Query plan rendering
src/renderer/components/query/QueryEditor.vue- Monaco wrappersrc/renderer/components/query/QueryResults.vue- Result gridsrc/renderer/components/query/QueryTabs.vue- Tab management
7. Schema Browser
Tree structure:8. App Database (SQLite)
Purpose: Store app-level data locally Location: OS-specific:- macOS:
~/Library/Application Support/Zequel/zequel.db - Windows:
%APPDATA%/Zequel/zequel.db - Linux:
~/.config/Zequel/zequel.db
src/main/migrations/):
connections- Saved connections (excluding passwords)query_history- Query execution historysaved_queries- Saved SQL snippetssettings- App preferencesrecents- Recently accessed itemspinned_entities- Pinned tables/views
src/main/services/database.ts
9. Security
Credential Storage:- Passwords stored in OS keychain via
keytar:- macOS: Keychain
- Windows: Credential Vault
- Linux: Secret Service API / libsecret
- Never stored in plaintext or app database
contextIsolation: truein BrowserWindownodeIntegration: false- Renderer has no direct Node.js access
- All main process APIs exposed via preload’s
contextBridge
- SSH connections via
ssh2library - Supports key-based and password authentication
- Automatic tunnel cleanup on disconnect
Design Patterns
Composition Over Inheritance
Composables encapsulate reusable logic:Event-Driven Communication
IPC events for real-time updates:Factory Pattern (Database Drivers)
Connection manager creates driver instances:Performance Optimizations
Virtual Scrolling
TanStack Virtual renders only visible rows (handles millions of rows)
Lazy Loading
Schema tree nodes load on expand, not on connect
Connection Pooling
Reuse connections across queries in same session
Cursor Streaming
Fetch large datasets in chunks, not all at once
Monaco Code Splitting
Monaco editor loaded async, not in main bundle
V8 Cache
v8CacheOptions: 'bypassHeatCheck' for faster startupWindow Management
Multi-window support:- Each window = independent BrowserWindow
- Session state transferred via
WindowInitData - Connections shared across windows (connection manager tracks ownership)
- Window-specific menu state
src/main/services/windowManager.ts
Next Steps
Testing
Learn how to write and run tests
Database Adapters
Add support for new databases