LESSON 25分

ストーリー

高橋アーキテクト
良いAPIと悪いAPIの違いは分かった。次は具体的なテクニックだ

高橋アーキテクトがチェックリストを取り出した。

高橋アーキテクト
API設計にはいくつかの定番パターンがある。命名規則、ページネーション、フィルタリング、ソート。これらを押さえておけば、チーム内で一貫したAPIが設計できる
あなた
覚えることが多そうですね…
高橋アーキテクト
パターンを覚えるというより、考え方を身につけるんだ。一度身につければ、どんなAPIでも自然に良い設計ができるようになる

命名規則

URL設計の基本ルール

// ルール1: リソースは複数形の名詞
GET /api/v1/users           // Good
GET /api/v1/user            // Bad(単数形)
GET /api/v1/getUsers        // Bad(動詞)

// ルール2: ケバブケース(小文字 + ハイフン)
GET /api/v1/order-items     // Good
GET /api/v1/orderItems      // Bad(camelCase)
GET /api/v1/order_items     // Bad(snake_case)

// ルール3: ネストは2階層まで
GET /api/v1/users/123/orders          // Good(2階層)
GET /api/v1/users/123/orders/456      // Good(2階層 + ID)
GET /api/v1/users/123/orders/456/items/789  // Bad(3階層は深すぎ)

// 深いネストの代替案
GET /api/v1/order-items/789           // 直接アクセス
GET /api/v1/order-items?orderId=456   // クエリパラメータで絞り込み

JSON フィールドの命名

// camelCase で統一(JavaScript/TypeScript の慣習に合わせる)
{
  "userId": "usr_123",
  "firstName": "太郎",
  "lastName": "田中",
  "emailAddress": "tanaka@example.com",
  "createdAt": "2025-01-15T09:00:00Z",
  "isActive": true
}

// snake_case も許容される(Ruby/Python 系のAPI)
{
  "user_id": "usr_123",
  "first_name": "太郎",
  "last_name": "田中"
}

// 重要: プロジェクト内で統一すること

ページネーション

3つの方式

// 方式1: オフセットベース(最も一般的)
GET /api/v1/users?page=2&perPage=20

// レスポンス
{
  "data": [...],
  "meta": {
    "currentPage": 2,
    "perPage": 20,
    "totalCount": 150,
    "totalPages": 8
  }
}

// 方式2: カーソルベース(大規模データ向け)
GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20

// レスポンス
{
  "data": [...],
  "meta": {
    "nextCursor": "eyJpZCI6MTQzfQ",
    "hasMore": true
  }
}

// 方式3: キーセットページネーション
GET /api/v1/users?after=usr_123&limit=20

方式の使い分け

方式特徴適する場面
オフセットベース任意のページにジャンプ可能管理画面、小〜中規模データ
カーソルベースパフォーマンスが安定SNSのタイムライン、大規模データ
キーセットカーソルの改良版リアルタイムデータ

フィルタリング

クエリパラメータによるフィルタリング

// 基本的なフィルタ
GET /api/v1/products?category=electronics
GET /api/v1/products?minPrice=1000&maxPrice=5000
GET /api/v1/products?status=active

// 複数値のフィルタ(カンマ区切り)
GET /api/v1/products?category=electronics,books

// 日付範囲
GET /api/v1/orders?createdAfter=2025-01-01&createdBefore=2025-01-31

// 検索(部分一致)
GET /api/v1/users?search=tanaka
GET /api/v1/products?q=wireless+keyboard

TypeScript での実装例

interface ProductFilter {
  category?: string;
  minPrice?: number;
  maxPrice?: number;
  status?: 'active' | 'inactive' | 'draft';
  search?: string;
}

interface PaginationParams {
  page?: number;
  perPage?: number;
}

// フィルタをクエリパラメータに変換
function buildQueryString(
  filter: ProductFilter,
  pagination: PaginationParams
): string {
  const params = new URLSearchParams();

  if (filter.category) params.set('category', filter.category);
  if (filter.minPrice) params.set('minPrice', String(filter.minPrice));
  if (filter.maxPrice) params.set('maxPrice', String(filter.maxPrice));
  if (filter.status) params.set('status', filter.status);
  if (filter.search) params.set('search', filter.search);
  if (pagination.page) params.set('page', String(pagination.page));
  if (pagination.perPage) params.set('perPage', String(pagination.perPage));

  return params.toString();
}

// 使用例
const query = buildQueryString(
  { category: 'electronics', minPrice: 1000 },
  { page: 1, perPage: 20 }
);
// → "category=electronics&minPrice=1000&page=1&perPage=20"

ソート

ソートパラメータの設計

// 方式1: sort パラメータ(推奨)
GET /api/v1/products?sort=price         // 価格の昇順
GET /api/v1/products?sort=-price        // 価格の降順(- プレフィックス)
GET /api/v1/products?sort=-createdAt    // 作成日の降順

// 方式2: 複数フィールドでのソート
GET /api/v1/products?sort=-price,name   // 価格降順、同一価格は名前昇順

// 方式3: sortBy + sortOrder(明示的)
GET /api/v1/products?sortBy=price&sortOrder=desc

フィールド選択(Sparse Fieldsets)

// 必要なフィールドだけを取得(帯域の節約)
GET /api/v1/users/123?fields=name,email
// → { "name": "田中太郎", "email": "tanaka@example.com" }

// リレーションの展開
GET /api/v1/users/123?include=orders,profile
// → ユーザー情報に加えて、注文とプロフィールも含めて返す

コンテンツネゴシエーション

Accept ヘッダー

// クライアントが望むレスポンス形式を指定
GET /api/v1/users/123
Accept: application/json

// サーバーのレスポンス
Content-Type: application/json
{
  "id": "123",
  "name": "田中太郎"
}

// CSVで欲しい場合
GET /api/v1/reports/sales
Accept: text/csv

// PDFで欲しい場合
GET /api/v1/invoices/123
Accept: application/pdf

まとめ

ベストプラクティスルール
URL設計複数形の名詞、ケバブケース、ネスト2階層まで
JSON命名camelCase で統一
ページネーションオフセット or カーソル(規模に応じて選択)
フィルタリングクエリパラメータで条件指定
ソートsort パラメータ、- プレフィックスで降順
フィールド選択fields パラメータで必要なフィールドのみ取得
コンテンツネゴシエーションAccept ヘッダーで形式を指定

チェックリスト

  • URL設計の基本ルール(複数形、ケバブケース、ネスト制限)を理解した
  • 3つのページネーション方式の違いと使い分けを把握した
  • フィルタリングとソートのクエリパラメータ設計を理解した
  • フィールド選択とコンテンツネゴシエーションを把握した

次のステップへ

API設計のベストプラクティスを学びました。

次のセクションでは、エラーハンドリングの設計を学びます。 クライアントに適切なエラー情報を返すことは、API設計者の重要な責務です。


推定読了時間: 25分