cherry-studio/packages/shared/data/api/README.md
fullex 18df6085d7 refactor(dataApi): streamline Data API schema and type definitions
- Removed outdated API model and schema files to simplify the structure.
- Consolidated API types and schemas for better organization and clarity.
- Updated import paths across the codebase to reflect the new structure.
- Enhanced documentation in related README files to guide usage of the new API schema organization.
2025-12-26 12:52:32 +08:00

5.6 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
├── errorCodes.ts      # Error handling utilities and factories
└── schemas/
    ├── index.ts       # Schema composition (merges all domain schemas)
    ├── test.ts        # Test API schema and DTOs
    └── batch.ts       # Batch/transaction API schema

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})
errorCodes.ts DataApiErrorFactory, error codes, and error handling utilities
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,
  ErrorCode
} from '@shared/data/api'

import { DataApiErrorFactory, isDataApiError } 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

  1. 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 response field for each endpoint
  • Invalid schemas cause TypeScript errors at the composition point
  1. Register in schemas/index.ts:
import type { TopicSchemas } from './topic'

// AssertValidSchemas provides fallback validation even if ValidateSchema is forgotten
export type ApiSchemas = AssertValidSchemas<TestSchemas & BatchSchemas & TopicSchemas>
  1. 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

Use DataApiErrorFactory for consistent error creation:

import { DataApiErrorFactory, ErrorCode } from '@shared/data/api'

// Create errors
throw DataApiErrorFactory.notFound('Topic', id)
throw DataApiErrorFactory.validationError('Name is required')
throw DataApiErrorFactory.fromCode(ErrorCode.DATABASE_ERROR, 'Connection failed')

// Check errors
if (isDataApiError(error)) {
  console.log(error.code, error.status)
}

Architecture Overview

Renderer                           Main
────────────────────────────────────────────────────
DataApiService  ──IPC──►  IpcAdapter  ──►  ApiServer
     │                                        │
     │                                        ▼
 ApiClient                              MiddlewareEngine
 (typed)                                      │
                                              ▼
                                         Handlers
                                         (typed)
  • Renderer: Uses DataApiService with type-safe ApiClient interface
  • IPC: Requests serialized via IpcAdapter
  • Main: ApiServer routes to handlers through MiddlewareEngine
  • Type Safety: End-to-end types from client call to handler implementation