Files
CherryHQ-cherry-studio/docs/references/data/data-api-overview.md

158 lines
11 KiB
Markdown

# DataApi System Overview
The DataApi system provides type-safe IPC communication for business data operations between the Renderer and Main processes.
## Purpose
DataApiService handles data that:
- Is **business data accumulated through user activity**
- Has **dedicated database schemas/tables**
- Users can **create, delete, modify records** without fixed limits
- Would be **severe and irreplaceable** if lost
- Can grow to **large volumes** (potentially GBs)
## What DataApi is NOT For
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:
- **System control**: Window management, process control, app configuration changes
- **External service integration**: OAuth flows, WebDAV/S3 operations, backup/restore workflows
- **Imperative commands**: Sending notifications, opening URLs, launching external processes
- **Stateless queries without database backing**: System info, font lists, disk space checks
- **Side effects bundled into a data write**: fs/network/process/external-service work performed inside a handler or service that also writes the database — no matter how deeply nested (see [Hard Rule: No Non-Data Side Effects](./api-design-guidelines.md#hard-rule-no-non-data-side-effects))
**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](./api-design-guidelines.md#dataapi-scope--boundaries) for detailed anti-patterns.
## Key Characteristics
### Type-Safe Communication
- End-to-end TypeScript types from client call to handler
- Path parameter inference from route definitions
- Compile-time validation of request/response shapes
### RESTful-Style API
- Familiar HTTP semantics (GET, POST, PUT, PATCH, DELETE)
- Resource-based URL patterns (`/topics/:id/messages`)
- Standard status codes and error responses
### On-Demand Data Access
- No automatic caching (fetch fresh data when needed)
- Explicit cache control via query options
- Supports large datasets with pagination
## Architecture Diagram
```
┌────────────────────────────────────────────────────────────┐
│ 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 │ │
│ │ - 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 │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```
## Architecture Layers
### 1. API Layer (Handlers)
- **Location**: `src/main/data/api/handlers/`
- **Responsibility**: HTTP-like interface layer
- **Does**: Extract parameters, call services, transform responses
- **Does NOT**: Contain business logic
### 2. Service Layer (Services)
- **Location**: `src/main/data/services/`
- **Responsibility**: Domain logic, workflows, and data access
- **Does**: Validation, transaction coordination, orchestration, Drizzle ORM queries
- **Write atomicity**: use `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](./database-patterns.md#write-serialization-dbservicewithwritetx).
> **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](./data-api-in-main.md#registry-services-supplementary).
### 3. Database Layer
- **Location**: `src/main/data/db/`
- **Technology**: SQLite + Drizzle ORM
- **Schemas**: `db/schemas/` directory
### Repository Pattern (Strongly Discouraged)
> **⚠️ 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).
## Key Features
### Automatic Retry
- Exponential backoff for transient failures
- Configurable retry count and delays
- Skips retry for client errors (4xx)
### Error Handling
- Typed error codes (`ErrorCode` enum)
- `DataApiError` class with retryability detection
- Factory methods for consistent error creation
### Request Timeout
- Configurable per-request timeouts
- Automatic cancellation of stale requests
### Dynamic Paths & Cache Invalidation
- `useQuery` / `useMutation` / `useInfiniteQuery` / `usePaginatedQuery` accept either concrete paths (`/providers/abc`) or template paths with `params` (`/providers/:providerId`)
- Each pagination hook constrains its path generic to the matching pagination shape — mixing cursor and offset paths is a compile-time error
- `refresh` option supports static paths, `/*` prefix for fan-out, and function form for keys computed from args/result
- Details, patterns, and misuse warnings: see [DataApi in Renderer → Dynamic Paths & Refresh Patterns](./data-api-in-renderer.md#dynamic-paths)
## Usage Summary
For detailed code examples, see:
- [DataApi in Renderer](./data-api-in-renderer.md) - Client-side usage
- [DataApi in Main](./data-api-in-main.md) - Server-side implementation
- [API Design Guidelines](./api-design-guidelines.md) - RESTful conventions
- [API Types](./api-types.md) - Type system details