- Replaced `DataApiError` with `SerializedDataApiError` for improved error serialization and IPC transmission. - Enhanced error response format with additional fields for better context and debugging. - Updated error handling utilities to streamline error creation and retry logic. - Removed the deprecated `errorCodes.ts` file and migrated relevant functionality to `apiErrors.ts`. - Updated documentation to reflect changes in error handling practices and structures.
6.9 KiB
Data API Type System
This directory contains the type definitions and utilities for Cherry Studio's Data API system, which provides type-safe IPC communication between renderer and main processes.
Directory Structure
packages/shared/data/api/
├── index.ts # Barrel export for infrastructure types
├── apiTypes.ts # Core request/response types and API utilities
├── apiPaths.ts # Path template literal type utilities
├── apiErrors.ts # Error handling: ErrorCode, DataApiError class, factory
└── schemas/
├── index.ts # Schema composition (merges all domain schemas)
└── test.ts # Test API schema and DTOs
File Responsibilities
| File | Purpose |
|---|---|
apiTypes.ts |
Core types (DataRequest, DataResponse, ApiClient) and schema utilities |
apiPaths.ts |
Template literal types for path resolution (/items/:id → /items/${string}) |
apiErrors.ts |
ErrorCode enum, DataApiError class, DataApiErrorFactory, retryability config |
index.ts |
Unified export of infrastructure types (not domain DTOs) |
schemas/index.ts |
Composes all domain schemas into ApiSchemas using intersection types |
schemas/*.ts |
Domain-specific API definitions and DTOs |
Import Conventions
Infrastructure Types (via barrel export)
Use the barrel export for common API infrastructure:
import type {
DataRequest,
DataResponse,
ApiClient,
PaginatedResponse
} from '@shared/data/api'
import {
ErrorCode,
DataApiError,
DataApiErrorFactory,
isDataApiError,
toDataApiError
} from '@shared/data/api'
Domain DTOs (directly from schema files)
Import domain-specific types directly from their schema files:
// Topic domain
import type { Topic, CreateTopicDto, UpdateTopicDto } from '@shared/data/api/schemas/topic'
// Message domain
import type { Message, CreateMessageDto } from '@shared/data/api/schemas/message'
// Test domain (development)
import type { TestItem, CreateTestItemDto } from '@shared/data/api/schemas/test'
Adding a New Domain Schema
- Create the schema file (e.g.,
schemas/topic.ts):
import type { PaginatedResponse } from '../apiTypes'
// Domain models
export interface Topic {
id: string
name: string
createdAt: string
}
export interface CreateTopicDto {
name: string
}
// API Schema - validation happens via AssertValidSchemas in index.ts
export interface TopicSchemas {
'/topics': {
GET: {
response: PaginatedResponse<Topic> // response is required
}
POST: {
body: CreateTopicDto
response: Topic
}
}
'/topics/:id': {
GET: {
params: { id: string }
response: Topic
}
}
}
Validation: Schemas are validated at composition level via AssertValidSchemas in schemas/index.ts:
- Ensures only valid HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Requires
responsefield for each endpoint - Invalid schemas cause TypeScript errors at the composition point
Design Guidelines: Before creating new schemas, review the API Design Guidelines for path naming, HTTP methods, and error handling conventions.
- Register in
schemas/index.ts:
import type { TopicSchemas } from './topic'
// AssertValidSchemas provides fallback validation even if ValidateSchema is forgotten
export type ApiSchemas = AssertValidSchemas<TestSchemas & TopicSchemas>
- Implement handlers in
src/main/data/api/handlers/
Type Safety Features
Path Resolution
The system uses template literal types to map concrete paths to schema paths:
// Concrete path '/topics/abc123' maps to schema path '/topics/:id'
api.get('/topics/abc123') // TypeScript knows this returns Topic
Exhaustive Handler Checking
ApiImplementation type ensures all schema endpoints have handlers:
// TypeScript will error if any endpoint is missing
const handlers: ApiImplementation = {
'/topics': {
GET: async () => { /* ... */ },
POST: async ({ body }) => { /* ... */ }
}
// Missing '/topics/:id' would cause compile error
}
Type-Safe Client
ApiClient provides fully typed methods:
const topic = await api.get('/topics/123') // Returns Topic
const topics = await api.get('/topics', { query: { page: 1 } }) // Returns PaginatedResponse<Topic>
await api.post('/topics', { body: { name: 'New' } }) // Body is typed as CreateTopicDto
Error Handling
The error system provides type-safe error handling with automatic retryability detection:
import {
DataApiError,
DataApiErrorFactory,
ErrorCode,
isDataApiError,
toDataApiError
} from '@shared/data/api'
// Create errors using the factory (recommended)
throw DataApiErrorFactory.notFound('Topic', id)
throw DataApiErrorFactory.validation({ name: ['Name is required'] })
throw DataApiErrorFactory.timeout('fetch topics', 3000)
throw DataApiErrorFactory.database(originalError, 'insert topic')
// Or create directly with the class
throw new DataApiError(
ErrorCode.NOT_FOUND,
'Topic not found',
404,
{ resource: 'Topic', id: 'abc123' }
)
// Check if error is retryable (for automatic retry logic)
if (error instanceof DataApiError && error.isRetryable) {
await retry(operation)
}
// Check error type
if (error instanceof DataApiError) {
if (error.isClientError) {
// 4xx - issue with the request
} else if (error.isServerError) {
// 5xx - server-side issue
}
}
// Convert any error to DataApiError
const apiError = toDataApiError(unknownError, 'context')
// Serialize for IPC (Main → Renderer)
const serialized = apiError.toJSON()
// Reconstruct from IPC response (Renderer)
const reconstructed = DataApiError.fromJSON(response.error)
Retryable Error Codes
The following errors are automatically considered retryable:
SERVICE_UNAVAILABLE(503)TIMEOUT(504)RATE_LIMIT_EXCEEDED(429)DATABASE_ERROR(500)INTERNAL_SERVER_ERROR(500)RESOURCE_LOCKED(423)
Architecture Overview
Renderer Main
────────────────────────────────────────────────────
DataApiService ──IPC──► IpcAdapter ──► ApiServer
│ │
│ ▼
ApiClient MiddlewareEngine
(typed) │
▼
Handlers
(typed)
- Renderer: Uses
DataApiServicewith type-safeApiClientinterface - IPC: Requests serialized via
IpcAdapter - Main:
ApiServerroutes to handlers throughMiddlewareEngine - Type Safety: End-to-end types from client call to handler implementation