mcpField
D
designing-sdks

designing-sdks

github.com/ancoleman開発52571 回インストール46 週間前に更新
リポジトリ

designing-sdks について

クライアントライブラリを開発するエンジニア向けの設計ガイドです。API構成、認証、エラー処理、バージョン管理を扱い、TypeScript、Python、Goのパターンと例を掲載しています。

主な機能

  1. 01リソース型・コマンド型の構成
  2. 02言語別クライアントパターン
  3. 03認証とトークン更新
  4. 04リトライ、バックオフ、エラー処理
  5. 05自動・手動ページネーション
  6. 06ストリーミングAPIとバージョン管理

活用シーン

  1. 01REST API用クライアントライブラリの設計
  2. 02一時的なエラーの再試行とレート制限対応
  3. 03ページネーション、認証、ストリーミングの実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ancoleman/ai-design-components --skill designing-sdks

インストール後、Claude Code で/designing-sdksを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

outputs.yaml
examples/go/
basic-client.go
channel-pagination.go
context-client.go
examples/python/
async-client.py
dual-client.py
sync-client.py
examples/typescript/
advanced-client.ts
basic-client.ts
resource-based.ts
references/
architecture-patterns.md
authentication.md
error-handling.md
pagination.md
retry-backoff.md
testing-sdks.md
versioning.md

詳細

SDK の設計

直感的な API、堅牢なエラー処理、自動再試行、プログラミング言語間で一貫したパターンを通じて、優れた開発者体験を実現するクライアントライブラリ(SDK)を設計します。

このスキルを使用する場面

REST API のクライアントライブラリを構築する場合、内部サービスの SDK を作成する場合、指数バックオフによる再試行ロジックを実装する場合、認証パターンを処理する場合、型付きエラー階層を作成する場合、非同期イテレーターによるページネーションを実装する場合、またはリアルタイムデータ向けのストリーミング API を設計する場合に使用します。

中核となるアーキテクチャパターン

クライアント → リソース → メソッド

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 スタイル):

const client = new APIClient({ apiKey: 'sk_test_...' })
const user = await client.users.create({ email: '[email protected]' })

メソッド数が 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: '[email protected]' })

すべてのメソッドは Promise を返します。コールバックは避けてください。

Python:同期/非同期の両方
# Sync
client = APIClient(api_key='sk_test_...')
user = client.users.create(email='[email protected]')

# Async
async_client = AsyncAPIClient(api_key='sk_test_...')
user = await async_client.users.create(email='[email protected]')

両方のクライアントを提供します。ユーザーはアーキテクチャに応じて選択できます。

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 は有効期限が切れる前にトークンを自動更新します。

リクエストごとのベアラートークン
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('\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)
}

冪等性キー

再試行時の操作の重複を防ぎます。

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

サーバーはキーを使ってリクエストの重複を排除します。

バージョン管理

セマンティックバージョニング
  • 1.0.0 → 1.1.0:新機能(安全)
  • 1.1.0 → 2.0.0:破壊的変更(要確認)
  • 1.0.0 → 1.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 更新ユーザーベースの認証
リクエストごとのベアラーマルチテナント
再試行戦略
戦略用途
指数バックオフデフォルトの再試行
レート制限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):リソースベース、型付きエラー、優れた DX
  • 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)を参考にしてください。

コメント