npm package: @laterinc/query-kit
Description: Production-ready API client toolkit built on React Query with WebSocket, offline support, and advanced features
Author: Later Inc
Latest version: 0.3.1

# @laterinc/query-kit

生产级 API 客户端工具库，基于 React Query，支持 WebSocket、离线队列、熔断器等企业级特性。

## 安装

```bash
pnpm add @laterinc/query-kit @tanstack/react-query react
```

## 快速开始

```tsx
import { QueryKitProvider, useRequest } from '@laterinc/query-kit';

function App() {
  return (
    <QueryKitProvider config={{ baseURL: 'https://api.example.com' }}>
      <UserList />
    </QueryKitProvider>
  );
}
```


function UserList() {
  const { data, isLoading } = useRequest(['users'], '/users');
  return <div>{/* ... */}</div>;
}
```

## 核心功能

- **请求管理**：GET/POST/PUT/PATCH/DELETE、文件上传下载、请求取消
- **认证**：通用 AuthAdapter 接口，支持 JWT、Cookie、OAuth、API Key
- **错误处理**：401/403/429/503 自动处理，可配置回调
- **WebSocket**：`useSubscription` 实时订阅
- **分页**：页码分页、游标分页、无限滚动

## 配置

### 基础配置

```tsx
<QueryKitProvider
  config={{
    baseURL: 'https://api.example.com',
    apiPrefix: '/api',
    timeout: 60000,
  }}
>
```

### 认证配置

**无认证（公开 API）：**
```tsx
config={{ baseURL: '/api' }}
```

**JWT 认证**（需安装 `@laterinc/query-kit-auth`）：
```tsx
import { createJwtAuthAdapter } from '@laterinc/query-kit-auth';

const adapter = createJwtAuthAdapter({
  refreshTokenUri: '/api/v1/auth/refresh',
  baseURL: 'https://api.example.com',
  loginUrl: '/login',
});

<QueryKitProvider config={{
  baseURL: 'https://api.example.com',
  auth: { adapter },
}}>
```

**自定义认证（Cookie / API Key）：**
```tsx
const adapter = {
  getCredentials: () => ({ 'X-API-Key': apiKey }),
  onUnauthorized: () => window.location.href = '/login',
};

config={{ auth: { adapter } }}
```

### Logger / 审计（企业级）

```tsx
<QueryKitProvider
  config={{
    baseURL: '/api',
    logger: {
      warn: (msg, meta) => Sentry.captureMessage(msg, { extra: meta }),
      error: (msg, meta) => Sentry.captureException(new Error(msg), { extra: meta }),
    },
    audit: {
      onRequestComplete: (meta) => {
        // 合规审计、统计上报
        analytics.track('api_request', meta);
      },
    },
    redact: {
      keys: ['password', 'token', 'authorization'],
    },
  }}
>
```

### 重试 / 速率限制 / 追踪 / Toast

```tsx
<QueryKitProvider
  config={{
    baseURL: '/api',
    retry: {
      maxRetries: 3,
      initialDelay: 1000,
      maxDelay: 30000,
      retryableStatuses: [429, 500, 502, 503, 504],
      useRetryAfter: true,
    },
    rateLimit: { maxRequests: 100, windowMs: 1000 },
    enableTracing: true, // 需安装 @opentelemetry/api
    toast: customToastAdapter, // 替换 UI 或做 i18n
  }}
>
```

### 健康检查 / Mock / 单次超时

```tsx
// 健康检查
const { data } = useHealthCheck('/health', { refetchInterval: 30000 });

// Mock 运输层（测试用）
const { transport } = createMockTransport({ ok: true });
const client = createRequest({ transport });

// 单次请求超时
await request.get('/slow', { timeout: 5000 });
```

## API 概览

| 模块 | 导出 |
|------|------|
| 查询 | `useRequest`, `usePaginatedRequest`, `useHealthCheck`, ... |
| 变更 | `useMutation`, `api`, `createAPI` |
| 请求 | `request`, `createRequest` |
| 认证 | `setDefaultAuthAdapter`, `getDefaultAuthAdapter` |
| 日志 | `setLogger`, `getLogger` |
| Toast | `setToastAdapter`, `getToast` |
| Mock | `createMockTransport` |

生成 API 文档：`pnpm docs`

## SSR（Next.js / 服务端渲染）

### prefetchQuery + Hydration

在服务端预取数据，客户端 hydration 避免闪烁：

```tsx
// app/users/page.tsx (Next.js App Router)
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
import { createQueryClient, prefetchQuery, QueryKitProvider } from '@laterinc/query-kit';

export default async function UsersPage() {
  const queryClient = createQueryClient({ timeout: 10000 });
  await prefetchQuery(queryClient, ['users'], '/users');

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <QueryKitProvider config={{ baseURL: '/api' }} queryClient={queryClient}>
        <UserList />
      </QueryKitProvider>
    </HydrationBoundary>
  );
}

function UserList() {
  const { data } = useRequest(['users'], '/users');
  return <ul>{data?.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
// 若 UserList 在单独文件，需加 'use client'
```

### 说明

- `createQueryClient` 创建与 Provider 一致的 QueryClient
- `prefetchQuery` 在服务端发起请求并写入缓存
- `HydrationBoundary` 将缓存传递给客户端
- 客户端 `useRequest` 直接命中缓存，无加载态

## 测试工具

```tsx
// 需安装 @testing-library/react
import { renderWithQueryKit, createQueryKitWrapper } from '@laterinc/query-kit/testing';

test('fetches users', async () => {
  const { getByText } = renderWithQueryKit(<UserList />, {
    config: { baseURL: '/api' },
  });
  await waitFor(() => expect(getByText('Alice')).toBeInTheDocument());
});
```

## Error Boundary

```tsx
import { ApiErrorBoundary } from '@laterinc/query-kit';

<ApiErrorBoundary fallback={(error, reset) => (
  <div>
    <p>{error.message}</p>
    <button onClick={reset}>重试</button>
  </div>
)}>
  <DataFetchingComponent />
</ApiErrorBoundary>
```

## Bundle 分析

```bash
pnpm analyze  # 生成 dist/stats.html
```

## 迁移指南

详见 [docs/MIGRATION.md](docs/MIGRATION.md)，从 axios/fetch 迁移步骤。

## 插件

- `@laterinc/query-kit/plugins/batch-operations` - 批量请求
- `@laterinc/query-kit/plugins/circuit-breaker` - 熔断器
- `@laterinc/query-kit/plugins/offline-manager` - 离线队列
- `@laterinc/query-kit/plugins/performance-monitor` - 性能监控

## License

MIT