S
Shineos Tech Blog
プロダクト開発におけるAPI設計のベストプラクティス - RESTful/GraphQLのデザインパターン

プロダクト開発におけるAPI設計のベストプラクティス - RESTful/GraphQLのデザインパターン

| Shineos Dev Team
Share:

はじめに

プロダクト開発において、APIは単なるデータ通信の手段ではなく、サービスの価値を外部や他のシステムに提供するための重要なインターフェースです。不適切なAPI設計は、開発効率の低下、バグの温床、将来的な拡張性の欠如といった技術的負債に直結します。

この記事では、長期的に運用可能で開発者体験(DX)の高いAPIを構築するためのAPI設計のベストプラクティスについて、RESTful APIとGraphQLの使い分けを中心に解説します。

API設計のベストプラクティスとは?

API設計のベストプラクティスとは、開発者が直感的に理解でき、使いやすく、かつ保守しやすいAPIを構築するためのルールや慣習の集合体です。

まとめ

主要な設計原則を以下にまとめました。

設計原則概要メリット
一貫性エンドポイント、命名規則、エラー応答の統一予測可能で学習コストが下がる
バージョニング破壊的変更への対応策を予め組み込むクライアントアプリの互換性を維持
適切な抽象化DBスキーマを直接露出させずにリソースとして設計内部実装の変更に強いAPIになる
ドキュメントOpenAPI (Swagger) などで仕様を明確化コミュニケーションコストの削減

RESTful API設計の基本原則

REST(Representational State Transfer)は、現在最も普及しているWeb APIの設計スタイルです。

1. リソース指向のURL設計

URLは「動作(動詞)」ではなく「リソース(名詞)」を表すべきです。操作はHTTPメソッド(GET, POST, PUT, DELETE)で表現します。

  • 良い例: GET /users/123/orders (ユーザー123の注文一覧を取得)
  • 悪い例: POST /getAdminUserOrders (動詞がURLに含まれている)

2. HTTPステータスコードの適切な使用

すべての成功を 200 OK で返すのではなく、状況に応じたステータスコードを返却します。

  • 200 OK: 成功(GET, PUTなど)
  • 201 Created: リソース作成成功(POST)
  • 204 No Content: 削除成功など、本文がない場合
  • 400 Bad Request: クライアント側の入力ミス
  • 401 Unauthorized: 認証が必要
  • 403 Forbidden: 権限不足
  • 404 Not Found: リソースが存在しない

3. 深いネストを避ける

リソースの関係性が深くなっても、URLの階層は浅く保つことが推奨されます。

// 複雑すぎるネスト(避けるべき)
GET /companies/1/departments/5/employees/99/projects

// フラットな設計(推奨)
GET /employees/99/projects
// またはクエリパラメータでフィルタリング
GET /projects?employee_id=99

GraphQLの台頭と使い分け

GraphQLは、クライアントが必要なデータを明示的に要求できるクエリ言語です。

GraphQLのメリット

  • オーバーフェッチ/アンダーフェッチの解消: 必要なフィールドだけを取得できるため、通信量を削減できます。
  • 単一エンドポイント: 複数のリソースを一度のリクエストで取得可能です。
  • 型システム: スキーマ定義により、クライアントとサーバー間で型安全な開発が可能です。

REST vs GraphQL 比較

RESTful APIとGraphQLのアーキテクチャ比較

項目RESTGraphQL
データ取得エンドポイントごとに固定クライアントが柔軟に指定可能
キャッシングHTTP標準キャッシュが利用しやすいクライアント側で工夫が必要
複雑さシンプルで理解しやすいクエリ組み立てやN+1問題への対処が必要
ユースケース一般的なWebサービス、マイクロサービス間通信複雑なデータ要件を持つFrontend、モバイルアプリ

実装例: Express.jsでのRESTful API

Node.jsのExpressフレームワークを使用した、基本的なRESTful APIの実装例です。

const express = require('express');
const app = express();
app.use(express.json());

// リソース: Items
const items = [
  { id: 1, name: 'Item A', price: 1000 },
  { id: 2, name: 'Item B', price: 2000 }
];

// GET: 一覧取得
app.get('/api/v1/items', (req, res) => {
  // ページネーション等のフィルタリングロジックをここに実装
  res.status(200).json({ data: items });
});

// GET: 詳細取得
app.get('/api/v1/items/:id', (req, res) => {
  const item = items.find(i => i.id === parseInt(req.params.id));
  if (!item) {
    return res.status(404).json({ error: { code: 'NOT_FOUND', message: 'Item not found' } });
  }
  res.status(200).json({ data: item });
});

// POST: 新規作成
app.post('/api/v1/items', (req, res) => {
  const newItem = { id: items.length + 1, ...req.body };
  items.push(newItem);
  // LocationヘッダーにリソースURLを含めるのがベストプラクティス
  res.status(201).header('Location', `/api/v1/items/${newItem.id}`).json({ data: newItem });
});

app.listen(3000, () => console.log('Server running on port 3000'));

ドキュメント化とOpenAPI (Swagger)

API設計を図解やテキストだけで終わらせず、機械可読な仕様書として残すことが重要です。OpenAPI Specification (以前のSwagger) はそのデファクトスタンダードです。

  • API仕様書からコード生成: クライアントSDKやサーバーのスタブコードを自動生成できます。
  • APIモックサーバー: 本実装を待たずにフロントエンド開発を開始できます。

おわりに

優れたAPI設計は、開発チーム全体の生産性を向上させ、将来の変更にも強い堅牢なシステムを作ります。

  • RESTful: リソース指向、HTTP準拠、シンプルさを重視する場合に選択。
  • GraphQL: 柔軟なデータ取得、通信最適化が必要な複雑なアプリに選択。
  • 共通: 一貫した命名、バージョニング、そして明確なドキュメント化(OpenAPI)を徹底すること。

プロジェクトの要件に合わせて最適なスタイルを選択し、利用者にとって使いやすいAPIを目指しましょう。

私たちShineosでは、新規プロダクト開発におけるアーキテクチャ設計やAPI開発の支援を行っています。ご興味のある方は、ぜひお気軽にご相談ください。

よくある質問

Q1. APIバージョンはURLパスとヘッダーのどちらに入れるべきですか?

一般的には URLパス (/api/v1/users) が最も直感的でブラウザでの確認もしやすいため推奨されます。ヘッダー (Accept: application/vnd.company.v1+json) を使う方法はRESTの原則には忠実ですが、クライアントの実装コストが若干高くなります。

Q2. GraphQLを採用するとN+1問題が起きやすいと聞きましたが?

はい、GraphQLのリゾルバがネストして呼び出される際にN+1問題が発生しがちです。これに対処するために、バックエンド側で DataLoader パターンを使用し、データベースへのクエリをバッチ化して実行する等の対策が必須です。

参考リンク