レスポンスフォーマット

APIレスポンス構造の理解

mindzieAPIのレスポンスフォーマット、ステータスコード、エラー処理パターン、およびデータ構造について学び、堅牢な連携を構築するための基礎を理解しましょう。

標準レスポンスフォーマット

すべてのmindzieAPIレスポンスは、一貫したJSON形式で予測可能な構造を持ちます。

成功レスポンス

{
  "data": {
    // 主なレスポンスデータ
  },
  "metadata": {
    "timestamp": "2024-01-15T10:30:00Z",
    "requestId": "req_12345",
    "version": "1.0.0"
  },
  "pagination": {
    // ページネーションがあるレスポンスの場合に存在
    "currentPage": 1,
    "totalPages": 5,
    "totalItems": 100,
    "itemsPerPage": 20,
    "hasNext": true,
    "hasPrevious": false
  }
}

エラーレスポンス

{
  "error": {
    "code": "validation_failed",
    "message": "リクエスト検証に失敗しました",
    "details": {
      "field": "datasetId",
      "reason": "無効なGUID形式です"
    },
    "timestamp": "2024-01-15T10:30:00Z",
    "requestId": "req_12345"
  }
}

レスポンスタイプ

成功レスポンス

HTTP 2xxステータスコード、構造化されたJSONデータとメタデータを含みます。

エラーレスポンス

HTTP 4xx/5xxステータスコード、詳細なエラー情報が含まれます。

ページネーション

大きなデータセットレスポンスに対して一貫したページネーション形式を適用しています。

HTTPステータスコード

成功コード (2xx)

コード ステータス 説明 用途
200 OK リクエスト成功、データ返却 GETリクエスト、成功した操作
201 Created リソースが正常に作成された 新規リソース作成のPOSTリクエスト
202 Accepted リクエストが非同期処理のため受理された 長時間処理、キューイングされたタスク
204 No Content リクエスト成功、返却データなし DELETEリクエスト、返却データなしの更新

クライアントエラーコード (4xx)

コード ステータス 説明 主な原因
400 Bad Request リクエストの形式またはパラメータが無効 ヘッダー不足、無効なJSON、不正なデータ形式
401 Unauthorized 認証が必要または認証失敗 トークン欠如/無効、資格情報の期限切れ
403 Forbidden 有効な認証だが権限不足 ユーザーアクセス制限、誤ったテナントやプロジェクト
404 Not Found 指定リソースが存在しない 無効なエンドポイント、存在しないリソースID
422 Unprocessable Entity フォーマットは正しいがビジネスロジック検証に失敗 ビジネスルール違反、制約違反
429 Too Many Requests レート制限超過 所定時間内のAPIコール過多

サーバーエラーコード (5xx)

コード ステータス 説明 対応
500 Internal Server Error 予期しないサーバーエラー 指数的バックオフでリトライ
502 Bad Gateway 上流サービスのエラー サービス状態の確認、後で再試行
503 Service Unavailable サービス一時的に利用不可 遅延後リトライ、メンテナンス確認
504 Gateway Timeout リクエストタイムアウト タイムアウト延長、最適化

共通レスポンスパターン

単一リソースレスポンス

{
  "actionId": "87654321-4321-4321-4321-210987654321",
  "actionType": "analyze",
  "status": "completed",
  "startTime": "2024-01-15T10:30:00Z",
  "endTime": "2024-01-15T10:32:15Z",
  "duration": 135,
  "result": {
    "outputId": "98765432-8765-4321-4321-987654321098",
    "recordsProcessed": 10000
  }
}

ページネーション付きコレクションレスポンス

{
  "actions": [
    {
      "actionId": "87654321-4321-4321-4321-210987654321",
      "actionType": "analyze",
      "status": "completed"
    },
    {
      "actionId": "11111111-2222-3333-4444-555555555555",
      "actionType": "export",
      "status": "processing"
    }
  ],
  "pagination": {
    "currentPage": 1,
    "totalPages": 5,
    "totalItems": 100,
    "itemsPerPage": 20,
    "hasNext": true,
    "hasPrevious": false,
    "links": {
      "first": "/api/Action/history?page=1&limit=20",
      "next": "/api/Action/history?page=2&limit=20",
      "last": "/api/Action/history?page=5&limit=20"
    }
  }
}

非同期処理レスポンス

{
  "operationId": "op_12345678-1234-1234-1234-123456789012",
  "status": "processing",
  "progress": {
    "percentage": 45,
    "currentStep": "data_analysis",
    "totalSteps": 5,
    "estimatedCompletion": "2024-01-15T10:35:00Z"
  },
  "trackingUrl": "/api/Execution/status/op_12345678-1234-1234-1234-123456789012",
  "message": "データセット分析を処理中..."
}

エラーレスポンス詳細

バリデーションエラー

{
  "error": {
    "code": "validation_failed",
    "message": "リクエスト検証に失敗しました",
    "details": {
      "errors": [
        {
          "field": "datasetId",
          "code": "invalid_format",
          "message": "有効なGUIDである必要があります"
        },
        {
          "field": "parameters.timeout",
          "code": "out_of_range",
          "message": "1秒から3600秒の間である必要があります"
        }
      ]
    },
    "timestamp": "2024-01-15T10:30:00Z",
    "requestId": "req_12345"
  }
}

認証エラー

{
  "error": {
    "code": "invalid_token",
    "message": "提供されたアクセストークンが無効または期限切れです",
    "details": {
      "tokenType": "bearer",
      "expiresAt": "2024-01-15T09:00:00Z",
      "suggestion": "アクセストークンを更新してください"
    },
    "timestamp": "2024-01-15T10:30:00Z",
    "requestId": "req_12345"
  }
}

レートリミットエラー

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "このエンドポイントのレート制限を超えました",
    "details": {
      "limit": 100,
      "remaining": 0,
      "resetTime": "2024-01-15T11:00:00Z",
      "retryAfter": 1800
    },
    "timestamp": "2024-01-15T10:30:00Z",
    "requestId": "req_12345"
  }
}

レスポンスヘッダー

標準ヘッダー

ヘッダー 説明
Content-Type レスポンスフォーマット application/json; charset=utf-8
X-Request-Id ユニークなリクエスト識別子 req_12345678
X-Response-Time サーバー処理時間 145ms
X-API-Version 利用中のAPIバージョン 1.0.0

レートリミット関連ヘッダー

ヘッダー 説明
X-RateLimit-Limit ウィンドウ内の最大リクエスト数 100
X-RateLimit-Remaining ウィンドウ内の残りリクエスト数 95
X-RateLimit-Reset リセットタイムスタンプ 1642251600
Retry-After 再試行までの待機秒数 3600

エラー処理のベストプラクティス

JavaScript例

async function handleAPIResponse(response) {
  // レスポンスが正常か確認
  if (!response.ok) {
    const errorData = await response.json();

    switch (response.status) {
      case 400:
        throw new ValidationError(errorData.error.message, errorData.error.details);
      case 401:
        throw new AuthenticationError('認証に失敗しました');
      case 403:
        throw new AuthorizationError('権限が不足しています');
      case 404:
        throw new NotFoundError('リソースが見つかりません');
      case 429:
        const retryAfter = response.headers.get('Retry-After');
        throw new RateLimitError(`レート制限に達しました。${retryAfter}秒後に再試行してください`);
      case 500:
      case 502:
      case 503:
      case 504:
        throw new ServerError('サーバーエラーが発生しました。再試行してください。');
      default:
        throw new APIError(`予期しないエラー: ${response.status}`);
    }
  }

  return await response.json();
}

// リトライロジック付きのAPI呼び出し
async function apiCallWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      return await handleAPIResponse(response);
    } catch (error) {
      if (error instanceof RateLimitError) {
        const retryAfter = parseInt(error.retryAfter) || Math.pow(2, attempt);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }

      if (error instanceof ServerError && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000; // 指数的バックオフ
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      throw error;
    }
  }
}

Python例

import requests
import time
from typing import Dict, Any

class APIError(Exception):
    def __init__(self, message: str, status_code: int = None, details: Dict = None):
        super().__init__(message)
        self.status_code = status_code
        self.details = details

def handle_api_response(response: requests.Response) -> Dict[str, Any]:
    """適切なエラー処理を伴うAPIレスポンスの処理"""

    if response.ok:
        return response.json()

    try:
        error_data = response.json()
    except ValueError:
        error_data = {"error": {"message": response.text}}

    error_info = error_data.get("error", {})
    message = error_info.get("message", f"HTTP {response.status_code}")
    details = error_info.get("details", {})

    if response.status_code == 429:
        retry_after = response.headers.get('Retry-After', '60')
        raise APIError(f"レート制限に達しました。{retry_after}秒後に再試行してください。",
                      response.status_code, details)

    elif response.status_code >= 500:
        raise APIError(f"サーバーエラー: {message}", response.status_code, details)

    elif response.status_code >= 400:
        raise APIError(f"クライアントエラー: {message}", response.status_code, details)

    raise APIError(f"予期しないエラー: {message}", response.status_code, details)

def api_call_with_retry(url: str, method: str = 'GET', max_retries: int = 3, **kwargs) -> Dict[str, Any]:
    """自動リトライロジック付きAPI呼び出し"""

    for attempt in range(1, max_retries + 1):
        try:
            response = requests.request(method, url, **kwargs)
            return handle_api_response(response)

        except APIError as e:
            if e.status_code == 429:
                retry_after = int(e.details.get('retryAfter', 60))
                time.sleep(retry_after)
                continue

            elif e.status_code >= 500 and attempt < max_retries:
                delay = 2 ** attempt  # 指数的バックオフ
                time.sleep(delay)
                continue

            raise

    raise APIError(f"最大リトライ回数({max_retries})に達しました")

APIデータ転送オブジェクト(DTO)

以下は、mindzieAPI各エンドポイントで使用される主要なDTO一覧です。

テナントDTO

TenantListItemDto

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation",
  "description": "メインテナント",
  "caseCount": 50000,
  "maxUserCount": 100,
  "maxAnalystCount": 20,
  "analystCount": 12,
  "userCount": 45,
  "preRelease": false,
  "isAcademic": false,
  "autoload": true,
  "dateCreated": "2024-01-15T10:30:00Z",
  "isDisabled": false
}

TenantDetailDto

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation",
  "description": "メインテナント",
  "isAcademic": false,
  "preRelease": false,
  "maxUserCount": 100,
  "maxAnalystCount": 20,
  "maxCases": 100000,
  "dateCreated": "2024-01-15T10:30:00Z",
  "isDisabled": false
}

TenantUpdatedDto

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation Updated",
  "message": "テナント 'acme-corp' が正常に更新されました",
  "isDisabled": false
}

ユーザーDTO

UserListItemDto

{
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "john.smith@example.com",
  "displayName": "John Smith",
  "firstName": "John",
  "lastName": "Smith",
  "roleName": "Analyst",
  "disabled": false,
  "isServiceAccount": false,
  "homeTenantId": null,
  "homeTenantName": null,
  "lastLogin": "2024-01-15T10:30:00Z",
  "tenantCount": 2,
  "tenantNames": "acme-corp, globex-inc",
  "dateCreated": "2024-01-01T00:00:00Z"
}

UserCreatedDto

{
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "john.smith@example.com",
  "displayName": "John Smith",
  "message": "ユーザーが正常に作成されました"
}

プロジェクトDTO

ProjectReturn

{
  "projectId": "87654321-4321-4321-4321-210987654321",
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "projectName": "Purchase Order Analysis",
  "projectDescription": "P2Pワークフローのプロセスマイニング分析",
  "dateCreated": "2024-01-15T10:30:00Z",
  "dateModified": "2024-01-20T14:45:00Z",
  "createdBy": "user-guid",
  "modifiedBy": "user-guid",
  "isActive": true,
  "datasetCount": 3,
  "investigationCount": 5,
  "dashboardCount": 2,
  "userCount": 8
}

ProjectSummaryReturn

{
  "projectId": "87654321-4321-4321-4321-210987654321",
  "projectName": "Purchase Order Analysis",
  "projectDescription": "プロセスマイニング分析",
  "dateCreated": "2024-01-15T10:30:00Z",
  "dateModified": "2024-01-20T14:45:00Z",
  "statistics": {
    "totalDatasets": 3,
    "totalInvestigations": 5,
    "totalDashboards": 2,
    "totalNotebooks": 12,
    "totalUsers": 8
  }
}

ProjectUserReturn

{
  "permissionId": "11111111-1111-1111-1111-111111111111",
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "john.smith@example.com",
  "displayName": "John Smith",
  "isOwner": true,
  "dateAssigned": "2024-01-15T10:30:00Z"
}

ProjectThumbnailReturn

{
  "projectId": "87654321-4321-4321-4321-210987654321",
  "hasThumbnail": true,
  "base64Image": "data:image/jpeg;base64,/9j/4AAQ..."
}

ProjectImportReturn

{
  "success": true,
  "projectId": "99999999-9999-9999-9999-999999999999",
  "projectName": "インポートされたプロジェクト",
  "datasetsImported": 2,
  "investigationsImported": 3,
  "dashboardsImported": 1,
  "errorMessage": null,
  "message": "プロジェクトが正常にインポートされました"
}

調査DTO

InvestigationReturn

{
  "investigationId": "11111111-2222-3333-4444-555555555555",
  "projectId": "87654321-4321-4321-4321-210987654321",
  "investigationName": "Order Analysis",
  "investigationDescription": "注文ワークフローのプロセスマイニング分析",
  "datasetId": "12345678-1234-1234-1234-123456789012",
  "dateCreated": "2024-01-15T10:30:00Z",
  "dateModified": "2024-01-20T14:45:00Z",
  "createdBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "modifiedBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "investigationOrder": 1.0,
  "isUsedForOperationCenter": false,
  "investigationFolderId": null,
  "notebookCount": 3
}

InvestigationListReturn

{
  "investigations": [
    {
      "investigationId": "11111111-2222-3333-4444-555555555555",
      "projectId": "87654321-4321-4321-4321-210987654321",
      "investigationName": "Order Analysis",
      "datasetId": "12345678-1234-1234-1234-123456789012",
      "notebookCount": 3
    }
  ],
  "totalCount": 5,
  "page": 1,
  "pageSize": 50
}

CreateInvestigationRequest

{
  "investigationName": "Order Analysis",
  "investigationDescription": "プロセスマイニング分析",
  "datasetId": "12345678-1234-1234-1234-123456789012",
  "isUsedForOperationCenter": false
}

UpdateInvestigationRequest

{
  "investigationName": "更新された分析名",
  "investigationDescription": "更新された説明",
  "isUsedForOperationCenter": true
}

ノートブックDTO

NotebookReturn

{
  "notebookId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "investigationId": "11111111-2222-3333-4444-555555555555",
  "name": "Main",
  "description": "主要分析ノートブック",
  "dateCreated": "2024-01-15T10:30:00Z",
  "dateModified": "2024-01-20T14:45:00Z",
  "createdBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "modifiedBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "notebookType": 0,
  "notebookOrder": 1.0,
  "lastExecutionDuration": 2.5,
  "blockCount": 12
}

NotebookListReturn

{
  "notebooks": [
    {
      "notebookId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
      "investigationId": "11111111-2222-3333-4444-555555555555",
      "name": "Main",
      "notebookOrder": 1.0,
      "blockCount": 12
    }
  ],
  "totalCount": 3
}

次のステップ

レスポンスフォーマットを理解したら、ActionsBlocks、またはDatasetsなどの特定のAPIセクションを探索し、これらのパターンがどのように実際に適用されているかを確認しましょう。