SDK设计与开发Skill designing-sdks

此技能专注于构建生产就绪的SDK客户端库,具备重试逻辑、错误处理、分页和多语言支持。关键词:SDK设计,客户端库,重试逻辑,错误处理,分页,多语言支持,开发者体验,API集成,软件开发,架构模式。

架构设计 0 次安装 0 次浏览 更新于 3/23/2026

名称: 设计SDKs 描述: 设计具备重试逻辑、错误处理、分页和多语言支持的生产就绪SDK。用于构建API的客户端库或创建面向开发者的SDK接口。

SDK设计

通过直观的API、健壮的错误处理、自动重试和跨编程语言的一致模式,设计具有卓越开发者体验的客户端库(SDKs)。

何时使用此技能

使用此技能当构建REST API的客户端库、创建内部服务SDK、实现具有指数退避的重试逻辑、处理认证模式、创建类型化错误层次结构、实现带有异步迭代器的分页,或设计用于实时数据的流式API时。

核心架构模式

客户端 → 资源 → 方法

按层次组织SDK代码:

客户端(配置:API密钥、基础URL、重试次数、超时)
├─ 资源(用户、支付、帖子)
│   ├─ create()、retrieve()、update()、delete()
│   └─ list()(带分页)
└─ 顶层方法(便利性)

基于资源(Stripe风格):

const client = new APIClient({ apiKey: 'sk_test_...' })
const user = await client.users.create({ email: 'user@example.com' })

用于少于100个方法的API。优先考虑开发者体验。

基于命令(AWS SDK v3):

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
await client.send(new PutObjectCommand({ Bucket: '...' }))

用于多于100个方法的API。优先考虑包大小和树摇优化。

详细架构指南,请见references/architecture-patterns.md

语言特定模式

TypeScript:仅异步

const user = await client.users.create({ email: 'user@example.com' })

所有方法返回Promise。避免回调。

Python:同步/异步双模式

# 同步
client = APIClient(api_key='sk_test_...')
user = client.users.create(email='user@example.com')

# 异步
async_client = AsyncAPIClient(api_key='sk_test_...')
user = await async_client.users.create(email='user@example.com')

提供两种客户端。用户根据架构选择。

Go:带上下文的同步

client := apiclient.New("api_key")
user, err := client.Users().Create(ctx, req)

使用context.Context处理超时和取消。

认证

API密钥(最常见)

const client = new APIClient({ apiKey: process.env.API_KEY })

将密钥存储在环境变量中,永不硬编码。

OAuth令牌刷新

const client = new APIClient({
  clientId: 'id',
  clientSecret: 'secret',
  refreshToken: 'token',
  onTokenRefresh: (newToken) => saveToken(newToken)
})

SDK在到期前自动刷新令牌。

每个请求的Bearer令牌

await client.users.list({
  headers: { Authorization: `Bearer ${userToken}` }
})

用于多租户应用。

OAuth流程、JWT处理和凭证提供者,请见references/authentication.md

重试和退避

带抖动的指数退避

async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries: number): Promise<T> {
  let attempt = 0

  while (attempt <= maxRetries) {
    try {
      return await fn()
    } catch (error) {
      attempt++
      if (attempt > maxRetries || !isRetryable(error)) throw error

      const exponential = Math.min(1000 * Math.pow(2, attempt - 1), 10000)
      const jitter = Math.random() * 500
      await sleep(exponential + jitter)
    }
  }
}

function isRetryable(error: any): boolean {
  return (
    error.code === 'ECONNRESET' ||
    error.code === 'ETIMEDOUT' ||
    (error.status >= 500 && error.status < 600) ||
    error.status === 429
  )
}

重试决策矩阵:

错误类型 重试? 原理
5xx、429、网络超时 ✅ 是 暂时性错误
4xx、401、403、404 ❌ 否 客户端错误不会自行修复

速率限制处理

if (error.status === 429) {
  const retryAfter = parseInt(error.headers['retry-after'] || '60')
  await sleep(retryAfter * 1000)
}

尊重429响应的Retry-After头。

抖动策略、断路器、幂等键,请见references/retry-backoff.md

错误处理

类型化错误层次结构

class APIError extends Error {
  constructor(
    message: string,
    public status: number,
    public code: string,
    public requestId: string
  ) {
    super(message)
    this.name = 'APIError'
  }
}

class RateLimitError extends APIError {
  constructor(message: string, requestId: string, public retryAfter: number) {
    super(message, 429, 'rate_limit_error', requestId)
  }
}

class AuthenticationError extends APIError {
  constructor(message: string, requestId: string) {
    super(message, 401, 'authentication_error', requestId)
  }
}

实践中的错误处理

try {
  const user = await client.users.create({ email: 'invalid' })
} catch (error) {
  if (error instanceof RateLimitError) {
    await sleep(error.retryAfter * 1000)
  } else if (error instanceof AuthenticationError) {
    console.error('Invalid API key')
  } else if (error instanceof APIError) {
    console.error(`${error.message} (Request ID: ${error.requestId})`)
  }
}

在所有错误中包含请求ID以便调试。

用户友好消息、验证错误和调试支持,请见references/error-handling.md

分页

异步迭代器(推荐)

TypeScript:

for await (const user of client.users.list({ limit: 100 })) {
  console.log(user.id, user.email)
}

Python:

async for user in client.users.list(limit=100):
    print(user.id, user.email)

SDK自动获取下一页。

实现

class UsersResource {
  async *list(options?: { limit?: number }): AsyncGenerator<User> {
    let cursor: string | undefined = undefined

    while (true) {
      const response = await this.client.request('GET', '/users', {
        query: { limit: String(options?.limit || 100), ...(cursor ? { cursor } : {}) }
      })

      for (const user of response.data) yield user

      if (!response.has_more) break
      cursor = response.next_cursor
    }
  }
}

手动分页

let cursor: string | undefined = undefined
while (true) {
  const response = await client.users.list({ limit: 100, cursor })
  for (const user of response.data) console.log(user.id)
  if (!response.has_more) break
  cursor = response.next_cursor
}

提供自动和手动选项。

游标与偏移分页和Go通道模式,请见references/pagination.md

流式处理

服务器发送事件

async *stream(path: string, body?: any): AsyncGenerator<any> {
  const response = await fetch(url, {
    headers: { 'Accept': 'text/event-stream' },
    body: JSON.stringify(body)
  })

  const reader = response.body!.getReader()
  const decoder = new TextDecoder()

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    const chunk = decoder.decode(value)
    for (const line of chunk.split('
')) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6)
        if (data === '[DONE]') return
        yield JSON.parse(data)
      }
    }
  }
}

// 用法
for await (const chunk of client.posts.stream({ prompt: 'Write a story' })) {
  process.stdout.write(chunk.content)
}

幂等键

防止重试期间的重复操作:

import { randomUUID } from 'crypto'

if (['POST', 'PATCH', 'PUT'].includes(method)) {
  headers['Idempotency-Key'] = options?.idempotencyKey || randomUUID()
}

// 用法
await client.charges.create(
  { amount: 1000 },
  { idempotencyKey: 'charge_unique_123' }
)

服务器通过键去重请求。

版本控制

语义版本控制

  • 1.0.01.1.0:新功能(安全)
  • 1.1.02.0.0:破坏性更改(需审查)
  • 1.0.01.0.1:错误修复(安全)

弃用警告

function deprecated(message: string, since: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value
    descriptor.value = function (...args: any[]) {
      console.warn(`[DEPRECATED] ${propertyKey} since ${since}. ${message}`)
      return originalMethod.apply(this, args)
    }
    return descriptor
  }
}

@deprecated('Use users.list() instead', 'v2.0.0')
async getAll() { return this.list() }

API版本固定

const client = new APIClient({
  apiKey: 'sk_test_...',
  apiVersion: '2025-01-01'
})

迁移策略,请见references/versioning.md

配置最佳实践

interface ClientConfig {
  apiKey: string
  baseURL?: string
  maxRetries?: number
  timeout?: number
  apiVersion?: string
  onTokenRefresh?: (token: string) => void
}

class APIClient {
  constructor(config: ClientConfig) {
    this.apiKey = config.apiKey
    this.baseURL = config.baseURL || 'https://api.example.com'
    this.maxRetries = config.maxRetries ?? 3
    this.timeout = config.timeout ?? 30000
  }
}

提供合理的默认值,仅要求apiKey。

快速参考表

认证模式

模式 使用案例
API密钥 服务到服务
OAuth刷新 基于用户的认证
每个请求的Bearer令牌 多租户

重试策略

策略 使用案例
指数退避 默认重试
速率限制 429响应
最大重试次数 避免无限循环(3-5次)

分页选项

模式 语言 使用案例
异步迭代器 TypeScript、Python 自动分页
生成器 Python 同步分页
通道 Go 并发迭代
手动 所有 显式控制

参考文档

架构:

  • references/architecture-patterns.md - 资源与命令组织

核心模式:

  • references/authentication.md - OAuth、令牌刷新、凭证提供者
  • references/retry-backoff.md - 指数退避、抖动、断路器
  • references/error-handling.md - 错误层次结构、调试支持
  • references/pagination.md - 游标与偏移、异步迭代器
  • references/versioning.md - SemVer、弃用策略
  • references/testing-sdks.md - 单元测试、模拟、集成测试

代码示例

TypeScript:

  • examples/typescript/basic-client.ts - 简单异步SDK
  • examples/typescript/advanced-client.ts - 重试、错误、流式
  • examples/typescript/resource-based.ts - Stripe风格组织

Python:

  • examples/python/sync-client.py - 同步客户端
  • examples/python/async-client.py - 异步客户端与asyncio
  • examples/python/dual-client.py - 同步和异步

Go:

  • examples/go/basic-client.go - 简单Go客户端
  • examples/go/context-client.go - 上下文模式
  • examples/go/channel-pagination.go - 基于通道的分页

最佳实践SDK示例

研究这些生产SDK:

TypeScript/JavaScript:

  • AWS SDK v3(@aws-sdk/client-*):模块化、可树摇、中间件
  • Stripe Node(stripe):基于资源、类型化错误、优秀开发者体验
  • OpenAI Node(openai):流式、异步迭代器、现代TypeScript

Python:

  • Boto3(boto3):资源与客户端模式、分页器
  • Stripe Python(stripe):同步/异步双模式、上下文管理器

Go:

  • AWS SDK Go v2(github.com/aws/aws-sdk-go-v2):上下文、中间件

常见陷阱

避免这些错误:

  1. 无重试逻辑 - 所有SDK都需要自动重试处理暂时性错误
  2. 错误消息不佳 - 包含请求ID、状态码、错误类型
  3. 无分页 - 实现带异步迭代器的自动分页
  4. 硬编码凭据 - 使用环境变量或配置文件
  5. 缺少幂等性 - 添加幂等键防止重复操作
  6. 忽略速率限制 - 尊重429响应的Retry-After
  7. 破坏性更改 - 使用SemVer、弃用前移除

与其他技能的集成

  • api-design-principles:API设计与SDK设计互补(错误码 → 错误类)
  • building-clis:CLI包装SDK用于命令行访问
  • testing-strategies:用模拟HTTP、重试场景测试SDK

下一步

查看语言特定示例以了解实现细节。研究参考文档以深入了解特定模式。检查最佳实践SDK(Stripe、AWS、OpenAI)以获取灵感。