docs/references/data/data-api-overview.md
The DataApi system provides type-safe IPC communication for business data operations between the Renderer and Main processes.
DataApiService handles data that:
DataApi must not be used as a general-purpose RPC layer. It is the data business-logic layer (persisting and querying records), not the application's business-logic layer. The following categories of operations belong in traditional IPC handlers (src/main/ipc.ts) or lifecycle services:
Why? DataApi's built-in retry, caching, and layered architecture (Handler → Service → SQLite) are designed for data persistence. These features become harmful or meaningless when applied to side-effectful operations. See API Design Guidelines — Scope & Boundaries for detailed anti-patterns.
/topics/:id/messages)┌────────────────────────────────────────────────────────────┐
│ Renderer Process │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ React Components │ │
│ │ - useQuery('/topics') │ │
│ │ - useMutation('/topics', 'POST') │ │
│ └──────────────────────────┬─────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ DataApiService (Renderer) │ │
│ │ - Type-safe ApiClient interface │ │
│ │ - Request serialization │ │
│ │ - Automatic retry with exponential backoff │ │
│ │ - Error handling and transformation │ │
│ └──────────────────────────┬─────────────────────────────┘ │
└────────────────────────────┼───────────────────────────────┘
│ IPC
┌────────────────────────────┼───────────────────────────────┐
│ Main Process ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ IpcAdapter │ │
│ │ - Receives IPC requests │ │
│ │ - Rejects untrusted senders (validateSender) │ │
│ │ - Routes to ApiServer │ │
│ └──────────────────────────┬─────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ ApiServer │ │
│ │ - Request routing by path and method │ │
│ │ - Middleware pipeline processing │ │
│ └──────────────────────────┬─────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Handlers (api/handlers/) │ │
│ │ - Thin layer: extract params, call service, transform │ │
│ │ - NO business logic here │ │
│ └──────────────────────────┬─────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Services (services/) │ │
│ │ - Business logic and validation │ │
│ │ - Transaction coordination │ │
│ │ - Data access via Drizzle ORM │ │
│ └──────────────────────────┬─────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ SQLite Database (via Drizzle ORM) │ │
│ │ - topic, message, file tables │ │
│ │ - Full-text search indexes │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
src/main/data/api/handlers/src/main/data/services/application.get('DbService').withWriteTx(fn) to commit multiple writes (or a read-then-write) all-or-nothing in one synchronous BEGIN IMMEDIATE transaction (fn must be synchronous — better-sqlite3 rejects a Promise-returning callback). A single autocommit write doesn't need it. See Database Patterns — Write Serialization.Note: In rare cases, a read-only Registry Service (e.g.,
ProviderRegistryService) may exist alongside Entity Services to merge preset data with DB data. See DataApi in Main — Registry Services.
src/main/data/db/db/schemas/ directory⚠️ Do NOT create Repository files by default. Services handle both business logic and data access directly via Drizzle ORM. This is an intentional design decision.
Only create a separate Repository when you are 1000% certain it is absolutely necessary — e.g., extremely complex multi-table queries with joins/CTEs that would make the Service unreadable, AND the query logic is reused across multiple services.
If in doubt, keep it in the Service. The overhead of an extra architectural layer is not justified for this project's scale (Electron desktop app + SQLite).
ErrorCode enum)DataApiError class with retryability detectionuseQuery / useMutation / useInfiniteQuery / usePaginatedQuery accept either concrete paths (/providers/abc) or template paths with params (/providers/:providerId)refresh option supports static paths, /* prefix for fan-out, and function form for keys computed from args/resultFor detailed code examples, see: