mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-03 12:27:41 +08:00
158 lines
11 KiB
Markdown
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
|