DevToolBox無料
ブログ

REST API ベストプラクティス:2026年完全ガイド

15分by DevToolBox

優れた REST API の構築は、現代の開発者にとって最も重要なスキルの一つです。優れた API は直感的で、一貫性があり、安全で、メンテナンスしやすいものです。この包括的なガイドでは、URL 命名規則や HTTP メソッドから認証、ページネーション、レート制限、よくある間違いまで、すべてを網羅しています。

URL 命名規則

良い REST API の基盤は、適切に設計された URL から始まります。エンドポイントは予測可能で、読みやすく、一貫したパターンに従う必要があります。

動詞ではなく名詞を使用

REST リソースは名詞で識別されるべきです。HTTP メソッドがすでにアクションを示しているため、URL パスに動詞を含める必要はありません。

# Good - nouns as resources
GET    /users          # Get all users
GET    /users/123      # Get user 123
POST   /users          # Create a new user
DELETE /users/123      # Delete user 123

# Bad - verbs in URLs
GET    /getUsers
POST   /createUser
DELETE /deleteUser/123

コレクションには複数形を使用

コレクションエンドポイントには常に複数形の名詞を使用してください。これにより、コレクション全体を参照する場合でも、その中の単一リソースを参照する場合でも一貫性が保たれます。

# Good - consistent plurals
GET /users          # Collection
GET /users/123      # Single resource in collection
GET /products       # Collection
GET /products/456   # Single resource

# Bad - inconsistent or singular
GET /user           # Ambiguous: one user or all users?
GET /user/123
GET /product-list

リレーションシップにはネストされたリソース

ネストを使用してリソース間の関係を表現しますが、2レベルより深くならないようにしてください。

# Good - clear parent-child relationship (max 2 levels)
GET /users/123/orders          # Orders belonging to user 123
GET /users/123/orders/456      # Specific order of user 123

# Bad - too deeply nested
GET /users/123/orders/456/items/789/reviews
# Better: use a direct resource endpoint
GET /order-items/789/reviews
# Or: flatten with query parameters
GET /reviews?order_item_id=789

URL パターン:良い例 vs 悪い例

良い悪い理由
GET /usersGET /getUsersHTTP method already implies the action
GET /users/123GET /user?id=123Use path parameters for resource identity
POST /usersPOST /createUserPOST already means create
PUT /users/123POST /updateUserPUT/PATCH for updates, use resource path
DELETE /users/123POST /deleteUserUse DELETE method, not POST with verb
GET /users/123/ordersGET /getUserOrders?userId=123Use nested resources for relationships
GET /products?category=electronicsGET /electronics/productsUse query params for filtering
GET /users?status=active&sort=nameGET /active-users-sorted-by-nameUse query params, not encoded URLs
PATCH /users/123POST /users/123/updateUse HTTP methods, not URL verbs
GET /users/123/avatarGET /getAvatarForUser/123Keep it resource-oriented

HTTP メソッド

各 HTTP メソッドには特定のセマンティクスがあります。正しく使用することで、API が予測可能になります。

メソッド目的冪等性リクエストボディ
GETRetrieve a resource or collectionYesNoGET /users/123
POSTCreate a new resourceNoYesPOST /users
PUTReplace a resource entirelyYesYesPUT /users/123
PATCHPartially update a resourceNo*YesPATCH /users/123
DELETERemove a resourceYesOptionalDELETE /users/123

* PATCH can be idempotent depending on implementation. If the patch describes the final state (e.g., {"name": "John"}), it is idempotent. If it describes an operation (e.g., {"op": "increment", "path": "/count"}), it is not.

HTTP ステータスコード

正しいステータスコードを使用することで、クライアントがレスポンスを適切に処理できるようになります。

コード名前使用するタイミング
200OKSuccessful GET, PUT, PATCH, or DELETE. Return the resource or confirmation.
201CreatedSuccessful POST that created a new resource. Include Location header with the new resource URL.
204No ContentSuccessful DELETE or PUT/PATCH when no response body is needed.
400Bad RequestThe request is malformed — invalid JSON, missing required fields, wrong data types.
401UnauthorizedNo valid authentication credentials provided. The client must authenticate first.
403ForbiddenThe client is authenticated but does not have permission for this action.
404Not FoundThe requested resource does not exist. Also use for hidden resources (instead of 403) for security.
409ConflictThe request conflicts with the current state — duplicate email, version mismatch, etc.
422Unprocessable EntityThe request is well-formed but fails validation — email format invalid, password too short.
429Too Many RequestsRate limit exceeded. Include Retry-After header with seconds until the client can retry.
500Internal Server ErrorAn unexpected server error occurred. Log the details server-side but do not expose them to clients.
503Service UnavailableThe server is temporarily unavailable — maintenance mode, overloaded. Include Retry-After header.

エラーハンドリング

一貫したエラーレスポンスにより、API が使いやすくなります。すべてのエラーは構造化された JSON ボディを返す必要があります。

標準エラーレスポンス形式

すべてのエラーレスポンスに一貫したエラーエンベロープを使用してください。

// Standard error response
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "The user with ID 123 was not found.",
    "status": 404,
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_abc123def456",
    "documentation_url": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
  }
}

// Another example: authentication error
{
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "The access token has expired. Please refresh your token.",
    "status": 401,
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_xyz789"
  }
}

フィールドレベルのバリデーションエラー

422 バリデーションエラーには、フィールドレベルのエラー情報を含めてください。

// 422 Validation error with field-level details
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The request body contains invalid fields.",
    "status": 422,
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address.",
        "rejected_value": "not-an-email"
      },
      {
        "field": "password",
        "message": "Must be at least 8 characters long.",
        "rejected_value": "short"
      },
      {
        "field": "age",
        "message": "Must be a positive integer.",
        "rejected_value": -5
      }
    ],
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_val456"
  }
}

認証と認可

適切な認証戦略の選択はユースケースによって異なります。最も一般的な3つのアプローチの比較です。

API キー

ヘッダーやクエリパラメータで渡されるシンプルな文字列トークン。サーバー間通信に最適です。

# API Key in header (recommended)
GET /api/users HTTP/1.1
Host: api.example.com
X-API-Key: sk_live_abc123def456ghi789

# API Key in query parameter (less secure - visible in logs)
GET /api/users?api_key=sk_live_abc123def456ghi789

JWT Bearer トークン

ユーザークレームを含む自己完結型トークン。マイクロサービスでのステートレス認証に最適です。

# JWT Bearer Token in Authorization header
GET /api/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

# Token refresh flow
POST /api/auth/refresh HTTP/1.1
Content-Type: application/json
{
  "refresh_token": "rt_abc123def456"
}

# Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_new789xyz"
}

OAuth 2.0

委任認可の業界標準。サードパーティアプリケーションがユーザーの代わりにリソースにアクセスできるようにします。

# OAuth 2.0 Authorization Code Flow

# Step 1: Redirect user to authorization server
GET https://auth.example.com/authorize?
  response_type=code&
  client_id=your_client_id&
  redirect_uri=https://yourapp.com/callback&
  scope=read:users write:users&
  state=random_csrf_token

# Step 2: Exchange authorization code for tokens
POST https://auth.example.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE_FROM_CALLBACK&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://yourapp.com/callback

# Step 3: Use the access token
GET /api/users/me HTTP/1.1
Authorization: Bearer ACCESS_TOKEN_HERE

認証方式の比較

機能API KeyJWTOAuth 2.0
ComplexityLowMediumHigh
StatelessYesYesDepends
User contextNoYesYes
Token expirationManual rotationBuilt-in (exp claim)Built-in
Scope/permissionsLimitedVia claimsFine-grained scopes
Third-party accessNoNoYes (delegated)
Best forServer-to-serverSPAs, Mobile appsThird-party integrations
RevocationDelete keyBlacklist neededRevoke at auth server

ページネーションパターン

リソースのリストを返すすべてのエンドポイントはページネーションをサポートする必要があります。

オフセットベースのページネーション

最もシンプルなアプローチ。ページ番号とリミットパラメータを使用します。

# Offset-based pagination request
GET /api/users?page=2&limit=20

# How it works internally
SELECT * FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET 20;  -- Skip first 20, return next 20

# Pros: Simple, allows jumping to any page
# Cons: Inconsistent with concurrent modifications,
#        slow on large offsets (OFFSET 100000)

カーソルベースのページネーション

不透明なカーソルトークンを使用してデータセット内の位置をマークします。リアルタイムデータに適しています。

# Cursor-based pagination request
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20

# The cursor is an opaque token (often base64-encoded)
# that points to a specific position in the dataset

# How it works internally (cursor decodes to {"id": 123})
SELECT * FROM users
WHERE id > 123
ORDER BY id ASC
LIMIT 20;

# Pros: Consistent results, fast on large datasets
# Cons: Cannot jump to arbitrary pages,
#        only next/previous navigation

ページネーションレスポンス形式

すべてのページネーションレスポンスにメタデータを含めてください。

// Offset-based pagination response
{
  "data": [
    { "id": 21, "name": "Alice" },
    { "id": 22, "name": "Bob" }
    // ... 18 more items
  ],
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total_pages": 15,
    "total_count": 294
  },
  "links": {
    "self": "/api/users?page=2&limit=20",
    "first": "/api/users?page=1&limit=20",
    "prev": "/api/users?page=1&limit=20",
    "next": "/api/users?page=3&limit=20",
    "last": "/api/users?page=15&limit=20"
  }
}

// Cursor-based pagination response
{
  "data": [
    { "id": 124, "name": "Charlie" },
    { "id": 125, "name": "Diana" }
    // ... 18 more items
  ],
  "meta": {
    "has_more": true,
    "next_cursor": "eyJpZCI6MTQzfQ",
    "prev_cursor": "eyJpZCI6MTI0fQ"
  },
  "links": {
    "next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
    "prev": "/api/users?cursor=eyJpZCI6MTI0fQ&limit=20&direction=prev"
  }
}

API バージョニング戦略

API は時間とともに進化します。バージョニングにより、既存のクライアントを中断することなく破壊的変更を行えます。

戦略利点欠点
URL Path/v1/users, /v2/usersExplicit, easy to route and cache, clear in logs and docsURL pollution, harder to sunset old versions
Custom HeaderAccept-Version: v2 or X-API-Version: 2Clean URLs, version hidden from casual usersHarder to test (need tools to set headers), not cacheable by default
Query Parameter/users?version=2Easy to test in browser, no URL path changesCan be accidentally omitted, harder to route, pollutes query string
Content NegotiationAccept: application/vnd.api.v2+jsonMost RESTful approach, per-resource versioningComplex, hard to test, poorly supported by many tools
# URL Path versioning (recommended)
GET /v1/users/123        # Version 1
GET /v2/users/123        # Version 2 with breaking changes

# Express.js router example
const v1Router = express.Router();
const v2Router = express.Router();

v1Router.get('/users/:id', v1UserController.get);
v2Router.get('/users/:id', v2UserController.get);

app.use('/v1', v1Router);
app.use('/v2', v2Router);

レート制限

レート制限は API を乱用から保護し、すべてのクライアントの公平な使用を確保します。

レート制限ヘッダー

クライアントが使用状況を監視できるよう、すべてのレスポンスにこれらのヘッダーを含めてください。

HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000          # Max requests per window
X-RateLimit-Remaining: 847       # Remaining requests in window
X-RateLimit-Reset: 1706176800    # Unix timestamp when window resets
X-RateLimit-Window: 3600         # Window duration in seconds (1 hour)

# Some APIs also include:
RateLimit-Policy: 1000;w=3600    # IETF draft standard format

429 リクエスト過多レスポンス

クライアントがレート制限を超えた場合、Retry-After ヘッダー付きの 429 ステータスを返します。

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 45
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706176800

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "You have exceeded the rate limit of 1000 requests per hour.",
    "status": 429,
    "retry_after": 45,
    "documentation_url": "https://api.example.com/docs/rate-limiting"
  }
}

バックオフ戦略

クライアントは 429 レスポンスを受け取った際、指数バックオフを実装する必要があります。

// Exponential backoff with jitter
async function fetchWithRetry(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    // Use Retry-After header if available
    const retryAfter = response.headers.get('Retry-After');
    let delay;

    if (retryAfter) {
      delay = parseInt(retryAfter, 10) * 1000;
    } else {
      // Exponential backoff: 1s, 2s, 4s, 8s, 16s
      const baseDelay = Math.pow(2, attempt) * 1000;
      // Add jitter (random 0-1s) to prevent thundering herd
      const jitter = Math.random() * 1000;
      delay = baseDelay + jitter;
    }

    console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Max retries exceeded');
}

セキュリティヘッダー

セキュリティヘッダーは API を一般的な Web 脆弱性から保護します。

ヘッダー推奨値目的
Access-Control-Allow-Originhttps://yourapp.com (specific origin)CORS: Controls which domains can call your API. Never use * in production with credentials.
Access-Control-Allow-MethodsGET, POST, PUT, PATCH, DELETECORS: Specifies which HTTP methods are allowed for cross-origin requests.
Access-Control-Allow-HeadersAuthorization, Content-TypeCORS: Specifies which request headers are allowed in cross-origin requests.
Strict-Transport-Securitymax-age=31536000; includeSubDomainsHSTS: Forces browsers to use HTTPS for all future requests to your domain.
Content-Security-Policydefault-src 'none'; frame-ancestors 'none'CSP: Prevents XSS and data injection attacks. For APIs, restrict everything.
X-Content-Type-OptionsnosniffPrevents browsers from MIME-type sniffing. Ensures responses are treated as their declared content type.
X-Frame-OptionsDENYPrevents your API responses from being embedded in iframes (clickjacking protection).
Cache-Controlno-store, no-cache, must-revalidatePrevents caching of sensitive API responses. Adjust per endpoint as needed.
// Express.js security headers middleware
const helmet = require('helmet');

app.use(helmet());

// Custom CORS configuration
const cors = require('cors');
app.use(cors({
  origin: ['https://yourapp.com', 'https://admin.yourapp.com'],
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
  maxAge: 86400  // Cache preflight for 24 hours
}));

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Cache-Control', 'no-store');
  next();
});

OpenAPI による API ドキュメント

良いドキュメントは API の採用に不可欠です。OpenAPI は REST API を記述するための標準です。

  • まず OpenAPI 仕様を書き(デザインファーストアプローチ)、次に仕様に合わせて API を実装します。
  • すべてのエンドポイント、パラメータ、レスポンススキーマに詳細な説明を含めてください。
  • すべてのリクエストとレスポンスボディにリアルな例の値を提供してください。
  • すべてのエンドポイントのエラーレスポンスを文書化してください。
  • タグを使用して関連エンドポイントをグループ化してください。
  • OpenAPI 仕様を API コードと一緒にバージョン管理してください。
  • インタラクティブなドキュメント(Swagger UI または Redoc)を設定してください。
# OpenAPI 3.1 specification example
openapi: 3.1.0
info:
  title: User Management API
  version: 1.0.0
  description: API for managing users and their resources

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List all users
      tags: [Users]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: A paginated list of users
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
              example:
                data:
                  - id: 1
                    name: "Alice Johnson"
                    email: "alice@example.com"
                meta:
                  current_page: 1
                  total_pages: 10
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      summary: Create a new user
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
            example:
              name: "Bob Smith"
              email: "bob@example.com"
              password: "securePassword123"
      responses:
        '201':
          description: User created successfully
        '422':
          $ref: '#/components/responses/ValidationError'

よくある REST API の間違い

経験豊富な開発者でもこれらの間違いを犯します。API がベストプラクティスに従っていることを確認してください。

#間違いなぜ問題か修正方法
1Using verbs in URLsREST is resource-oriented; HTTP methods express actionsUse nouns: /users not /getUsers
2Returning 200 for errorsClients rely on status codes for error handlingUse proper status codes: 400, 404, 422, 500
3Not paginating list endpointsReturns grow unbounded, causing timeouts and memory issuesAlways paginate with sensible defaults (limit=20)
4Exposing internal errorsStack traces reveal implementation details to attackersLog details server-side, return generic error messages
5Ignoring CORS headersBrowser-based clients cannot call your APIConfigure CORS for allowed origins and methods
6Not versioning the APIBreaking changes affect all clients simultaneouslyUse URL path versioning: /v1/users
7Using POST for everythingLoses HTTP method semantics, breaks caching and idempotencyUse GET, POST, PUT, PATCH, DELETE appropriately
8Inconsistent naming conventionsMixed case or formats confuse API consumersPick one (snake_case or camelCase) and be consistent
9No rate limitingAPI is vulnerable to abuse and DDoS attacksImplement rate limiting with clear headers
10Missing Content-Type headerClients cannot parse responses correctlyAlways set Content-Type: application/json

関連する開発者ツールを試す

よくある質問

REST と RESTful の違いは何ですか?

REST は Roy Fielding が定義したアーキテクチャスタイルです。RESTful は REST 制約に準拠する API を表す形容詞です。実際にはほとんどの「REST API」はすべての REST 制約に厳密に従っていませんが、用語は互換的に使われます。

リソースの更新には PUT と PATCH のどちらを使うべきですか?

クライアントがリソースの完全な置換を送信する場合は PUT を使用します。変更されたフィールドのみを送信する部分更新には PATCH を使用します。

API バージョニングはどのように処理すべきですか?

URL パスバージョニング(例:/v1/users)が最も一般的で明示的なアプローチです。理解しやすく、ルーティングやキャッシュも容易です。

REST API での認証を処理する最良の方法は何ですか?

ほとんどのアプリケーションでは、JWT Bearer トークンがセキュリティとシンプルさの良いバランスを提供します。短期アクセストークンとリフレッシュトークンローテーションを使用してください。

REST API でネストされたリソースをどのように設計すべきですか?

ネストを使用して親子関係を示します:/users/123/orders。ネストは最大2レベルまでに制限してください。

REST API レスポンスは null フィールドを含めるべきですか、省略すべきですか?

契約によります。null フィールドを含めるとスキーマが予測可能になります。省略するとペイロードサイズが減ります。一貫性を保ち、ドキュメント化してください。

𝕏 Twitterin LinkedIn
この記事は役に立ちましたか?

最新情報を受け取る

毎週の開発ヒントと新ツール情報。

スパムなし。いつでも解除可能。

Try These Related Tools

4xxHTTP Status Code Reference>>cURL to Code ConverterJWTJWT Decoder{ }JSON Formatter

Related Articles

HTTP ステータスコード:開発者のための完全リファレンスガイド

完全な HTTP ステータスコードリファレンス:1xx〜5xx の実用的解説、API ベストプラクティス、デバッグのコツ。

API認証:OAuth 2.0 vs JWT vs APIキー

API認証方式を比較:OAuth 2.0、JWT Bearerトークン、APIキー。各方式の使い分け、セキュリティトレードオフ、実装パターン。