designing-sdks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SDK Design

SDK设计

Design client libraries (SDKs) with excellent developer experience through intuitive APIs, robust error handling, automatic retries, and consistent patterns across programming languages.
通过直观的API、健壮的错误处理、自动重试机制以及跨编程语言的一致模式,设计具备卓越开发者体验的客户端库(SDK)。

When to Use This Skill

适用场景

Use when building a client library for a REST API, creating internal service SDKs, implementing retry logic with exponential backoff, handling authentication patterns, creating typed error hierarchies, implementing pagination with async iterators, or designing streaming APIs for real-time data.
适用于为REST API构建客户端库、创建内部服务SDK、实现带指数退避的重试逻辑、处理认证模式、创建类型化错误层级、通过异步迭代器实现分页,或为实时数据设计流式API等场景。

Core Architecture Patterns

核心架构模式

Client → Resources → Methods

客户端 → 资源 → 方法

Organize SDK code hierarchically:
Client (config: API key, base URL, retries, timeout)
├─ Resources (users, payments, posts)
│   ├─ create(), retrieve(), update(), delete()
│   └─ list() (with pagination)
└─ Top-Level Methods (convenience)
Resource-Based (Stripe style):
typescript
const client = new APIClient({ apiKey: 'sk_test_...' })
const user = await client.users.create({ email: 'user@example.com' })
Use for APIs <100 methods. Prioritizes developer experience.
Command-Based (AWS SDK v3):
typescript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
await client.send(new PutObjectCommand({ Bucket: '...' }))
Use for APIs >100 methods. Prioritizes bundle size and tree-shaking.
For detailed architectural guidance, see
references/architecture-patterns.md
.
按层级组织SDK代码:
Client (config: API key, base URL, retries, timeout)
├─ Resources (users, payments, posts)
│   ├─ create(), retrieve(), update(), delete()
│   └─ list() (with pagination)
└─ Top-Level Methods (convenience)
基于资源的模式(Stripe风格):
typescript
const client = new APIClient({ apiKey: 'sk_test_...' })
const user = await client.users.create({ email: 'user@example.com' })
适用于方法数少于100个的API,优先保障开发者体验。
基于命令的模式(AWS SDK v3风格):
typescript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
await client.send(new PutObjectCommand({ Bucket: '...' }))
适用于方法数多于100个的API,优先考虑包体积和摇树优化。
如需详细架构指导,请参阅
references/architecture-patterns.md

Language-Specific Patterns

语言特定模式

TypeScript: Async-Only

TypeScript:仅异步

typescript
const user = await client.users.create({ email: 'user@example.com' })
All methods return Promises. Avoid callbacks.
typescript
const user = await client.users.create({ email: 'user@example.com' })
所有方法均返回Promises,避免使用回调函数。

Python: Dual Sync/Async

Python:同步/异步双模式

python
undefined
python
undefined

Sync

同步

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

Async

异步

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

Provide both clients. Users choose based on architecture.
async_client = AsyncAPIClient(api_key='sk_test_...') user = await async_client.users.create(email='user@example.com')

同时提供两种客户端,用户可根据架构选择使用。

Go: Sync with Context

Go:带Context的同步模式

go
client := apiclient.New("api_key")
user, err := client.Users().Create(ctx, req)
Use context.Context for timeout and cancellation.
go
client := apiclient.New("api_key")
user, err := client.Users().Create(ctx, req)
使用context.Context实现超时和取消功能。

Authentication

认证机制

API Key (Most Common)

API密钥(最常用)

typescript
const client = new APIClient({ apiKey: process.env.API_KEY })
Store keys in environment variables, never hardcode.
typescript
const client = new APIClient({ apiKey: process.env.API_KEY })
将密钥存储在环境变量中,切勿硬编码。

OAuth Token Refresh

OAuth令牌刷新

typescript
const client = new APIClient({
  clientId: 'id',
  clientSecret: 'secret',
  refreshToken: 'token',
  onTokenRefresh: (newToken) => saveToken(newToken)
})
SDK automatically refreshes tokens before expiry.
typescript
const client = new APIClient({
  clientId: 'id',
  clientSecret: 'secret',
  refreshToken: 'token',
  onTokenRefresh: (newToken) => saveToken(newToken)
})
SDK会在令牌过期前自动刷新。

Bearer Token Per-Request

逐请求Bearer令牌

typescript
await client.users.list({
  headers: { Authorization: `Bearer ${userToken}` }
})
Use for multi-tenant applications.
See
references/authentication.md
for OAuth flows, JWT handling, and credential providers.
typescript
await client.users.list({
  headers: { Authorization: `Bearer ${userToken}` }
})
适用于多租户应用。
如需了解OAuth流程、JWT处理和凭证提供器,请参阅
references/authentication.md

Retry and Backoff

重试与退避

Exponential Backoff with Jitter

带抖动的指数退避

typescript
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
  )
}
Retry Decision Matrix:
Error TypeRetry?Rationale
5xx, 429, Network Timeout✅ YesTransient errors
4xx, 401, 403, 404❌ NoClient errors won't fix themselves
typescript
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❌ 否客户端错误无法通过重试解决

Rate Limit Handling

速率限制处理

typescript
if (error.status === 429) {
  const retryAfter = parseInt(error.headers['retry-after'] || '60')
  await sleep(retryAfter * 1000)
}
Respect
Retry-After
header on 429 responses.
See
references/retry-backoff.md
for jitter strategies, circuit breakers, and idempotency keys.
typescript
if (error.status === 429) {
  const retryAfter = parseInt(error.headers['retry-after'] || '60')
  await sleep(retryAfter * 1000)
}
在429响应时遵循
Retry-After
头信息。
如需了解抖动策略、断路器和幂等键,请参阅
references/retry-backoff.md

Error Handling

错误处理

Typed Error Hierarchy

类型化错误层级

typescript
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)
  }
}
typescript
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)
  }
}

Error Handling in Practice

实际错误处理示例

typescript
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})`)
  }
}
Include request ID in all errors for debugging.
See
references/error-handling.md
for user-friendly messages, validation errors, and debugging support.
typescript
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('无效API密钥')
  } else if (error instanceof APIError) {
    console.error(`${error.message} (请求ID: ${error.requestId})`)
  }
}
在所有错误中包含请求ID以方便调试。
如需了解用户友好型消息、验证错误和调试支持,请参阅
references/error-handling.md

Pagination

分页

Async Iterators (Recommended)

异步迭代器(推荐)

TypeScript:
typescript
for await (const user of client.users.list({ limit: 100 })) {
  console.log(user.id, user.email)
}
Python:
python
async for user in client.users.list(limit=100):
    print(user.id, user.email)
SDK automatically fetches next page.
TypeScript:
typescript
for await (const user of client.users.list({ limit: 100 })) {
  console.log(user.id, user.email)
}
Python:
python
async for user in client.users.list(limit=100):
    print(user.id, user.email)
SDK会自动获取下一页数据。

Implementation

实现方式

typescript
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
    }
  }
}
typescript
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
    }
  }
}

Manual Pagination

手动分页

typescript
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
}
Provide both automatic and manual options.
See
references/pagination.md
for cursor vs. offset pagination and Go channel patterns.
typescript
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

Streaming

流式传输

Server-Sent Events

Server-Sent Events

typescript
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('\n')) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6)
        if (data === '[DONE]') return
        yield JSON.parse(data)
      }
    }
  }
}

// Usage
for await (const chunk of client.posts.stream({ prompt: 'Write a story' })) {
  process.stdout.write(chunk.content)
}
typescript
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('\n')) {
      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)
}

Idempotency Keys

幂等键

Prevent duplicate operations during retries:
typescript
import { randomUUID } from 'crypto'

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

// Usage
await client.charges.create(
  { amount: 1000 },
  { idempotencyKey: 'charge_unique_123' }
)
Server deduplicates requests by key.
在重试期间防止重复操作:
typescript
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' }
)
服务器会根据键对请求进行去重。

Versioning

版本控制

Semantic Versioning

语义化版本控制

  • 1.0.0
    1.1.0
    : New features (safe)
  • 1.1.0
    2.0.0
    : Breaking changes (review)
  • 1.0.0
    1.0.1
    : Bug fixes (safe)
  • 1.0.0
    1.1.0
    :新增功能(兼容升级)
  • 1.1.0
    2.0.0
    :破坏性变更(需谨慎升级)
  • 1.0.0
    1.0.1
    :Bug修复(兼容升级)

Deprecation Warnings

弃用警告

typescript
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() }
typescript
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(`[已弃用] ${propertyKey}${since} 起弃用。 ${message}`)
      return originalMethod.apply(this, args)
    }
    return descriptor
  }
}

@deprecated('请改用users.list()', 'v2.0.0')
async getAll() { return this.list() }

API Version Pinning

API版本固定

typescript
const client = new APIClient({
  apiKey: 'sk_test_...',
  apiVersion: '2025-01-01'
})
See
references/versioning.md
for migration strategies.
typescript
const client = new APIClient({
  apiKey: 'sk_test_...',
  apiVersion: '2025-01-01'
})
如需了解迁移策略,请参阅
references/versioning.md

Configuration Best Practices

配置最佳实践

typescript
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
  }
}
Provide sensible defaults, require only apiKey.
typescript
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。

Quick Reference Tables

快速参考表格

Authentication Patterns

认证模式

PatternUse Case
API KeyService-to-service
OAuth RefreshUser-based auth
Bearer Per-RequestMulti-tenant
模式适用场景
API密钥服务间通信
OAuth刷新基于用户的认证
逐请求Bearer令牌多租户应用

Retry Strategies

重试策略

StrategyUse Case
Exponential BackoffDefault retry
Rate Limit429 responses
Max RetriesAvoid infinite loops (3-5)
策略适用场景
指数退避默认重试方案
速率限制处理429响应场景
最大重试次数避免无限循环(3-5次)

Pagination Options

分页选项

PatternLanguageUse Case
Async IteratorTypeScript, PythonAutomatic pagination
GeneratorPythonSync pagination
ChannelsGoConcurrent iteration
ManualAllExplicit control
模式语言适用场景
异步迭代器TypeScript、Python自动分页
生成器Python同步分页
通道Go并发迭代
手动分页所有语言显式控制分页

Reference Documentation

参考文档

Architecture:
  • references/architecture-patterns.md
    - Resource vs. command organization
Core Patterns:
  • references/authentication.md
    - OAuth, token refresh, credential providers
  • references/retry-backoff.md
    - Exponential backoff, jitter, circuit breakers
  • references/error-handling.md
    - Error hierarchies, debugging support
  • references/pagination.md
    - Cursor vs. offset, async iterators
  • references/versioning.md
    - SemVer, deprecation strategies
  • references/testing-sdks.md
    - Unit testing, mocking, integration tests
架构:
  • references/architecture-patterns.md
    - 资源型与命令型组织方式对比
核心模式:
  • references/authentication.md
    - OAuth、令牌刷新、凭证提供器
  • references/retry-backoff.md
    - 指数退避、抖动、断路器
  • references/error-handling.md
    - 错误层级、调试支持
  • references/pagination.md
    - 游标与偏移分页、异步迭代器
  • references/versioning.md
    - 语义化版本、弃用策略
  • references/testing-sdks.md
    - 单元测试、Mock、集成测试

Code Examples

代码示例

TypeScript:
  • examples/typescript/basic-client.ts
    - Simple async SDK
  • examples/typescript/advanced-client.ts
    - Retry, errors, streaming
  • examples/typescript/resource-based.ts
    - Stripe-style organization
Python:
  • examples/python/sync-client.py
    - Synchronous client
  • examples/python/async-client.py
    - Async client with asyncio
  • examples/python/dual-client.py
    - Both sync and async
Go:
  • examples/go/basic-client.go
    - Simple Go client
  • examples/go/context-client.go
    - Context patterns
  • examples/go/channel-pagination.go
    - Channel-based pagination
TypeScript:
  • examples/typescript/basic-client.ts
    - 简单异步SDK
  • examples/typescript/advanced-client.ts
    - 包含重试、错误处理、流式传输的高级SDK
  • 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
    - Context模式示例
  • examples/go/channel-pagination.go
    - 基于通道的分页实现

Best-in-Class SDK Examples

业界优秀SDK示例

Study these production SDKs:
TypeScript/JavaScript:
  • AWS SDK v3 (
    @aws-sdk/client-*
    ): Modular, tree-shakeable, middleware
  • Stripe Node (
    stripe
    ): Resource-based, typed errors, excellent DX
  • OpenAI Node (
    openai
    ): Streaming, async iterators, modern TypeScript
Python:
  • Boto3 (
    boto3
    ): Resource vs. client patterns, paginators
  • Stripe Python (
    stripe
    ): Dual sync/async, context managers
Go:
  • AWS SDK Go v2 (
    github.com/aws/aws-sdk-go-v2
    ): Context, middleware
参考以下生产级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
    ): Context支持、中间件架构

Common Pitfalls

常见陷阱

Avoid these mistakes:
  1. No Retry Logic - All SDKs need automatic retries for transient errors
  2. Poor Error Messages - Include request ID, status code, error type
  3. No Pagination - Implement automatic pagination with async iterators
  4. Hardcoded Credentials - Use environment variables or config files
  5. Missing Idempotency - Add idempotency keys to prevent duplicate operations
  6. Ignoring Rate Limits - Respect
    Retry-After
    header on 429 responses
  7. Breaking Changes - Use SemVer, deprecate before removing
避免以下错误:
  1. 无重试逻辑 - 所有SDK都需要针对临时错误的自动重试机制
  2. 错误信息质量差 - 需包含请求ID、状态码、错误类型
  3. 未实现分页 - 需通过异步迭代器实现自动分页
  4. 硬编码凭证 - 使用环境变量或配置文件存储凭证
  5. 缺少幂等性 - 添加幂等键以防止重复操作
  6. 忽略速率限制 - 在429响应时遵循
    Retry-After
    头信息
  7. 破坏性变更未处理 - 使用语义化版本控制,先弃用再移除功能

Integration with Other Skills

与其他技能的集成

  • api-design-principles: API design complements SDK design (error codes → error classes)
  • building-clis: CLIs wrap SDKs for command-line access
  • testing-strategies: Test SDKs with mocked HTTP, retry scenarios
  • api-design-principles: API设计与SDK设计相辅相成(错误码→错误类)
  • building-clis: CLI可基于SDK封装以提供命令行访问能力
  • testing-strategies: 使用Mock HTTP、重试场景测试SDK

Next Steps

下一步行动

Review language-specific examples for implementation details. Study references for deep dives on specific patterns. Examine best-in-class SDKs (Stripe, AWS, OpenAI) for inspiration.
查看语言特定示例获取实现细节。阅读参考文档深入了解特定模式。研究业界优秀SDK(Stripe、AWS、OpenAI)获取灵感。