API プロキシの設計と開発のベスト プラクティス

このドキュメントの目的は、Apigee Edge を使用した開発のための規範やベストプラクティスをひととおり紹介することです。ここでは、設計やコーディング、ポリシー使用、モニタリング、デバッグについて取り上げます。記載されている情報は、成功を収めた API プログラムを Apigee と協力して実装してきたデベロッパーによる経験の集積です。このドキュメントは完成版ではなく、今後も随時更新されます。

このガイドラインに加えて、Apigee Edge アンチパターンに関するコミュニティの投稿も役立つかもしれません。

開発の規範

コメントとドキュメント

  • ProxyEndpoint 構成や TargetEndpoint 構成にはインライン コメントを残します。とりわけ、Flow の根底にある機能をポリシー ファイル名があまりよく表していない場合、コメントがあるとその Flow のわかりやすさが向上します。
  • 有意義なコメントを残します。自明なコメントは避けてください。
  • インデント、スペースの挿入、縦方向の配置などに一貫性をもたせます。

フレームワークスタイルのコーディング

フレームワークスタイルのコーディングでは、API プロキシ リソースをバージョン管理システムに保存して、ローカル開発環境で再利用します。たとえば、ポリシーを再利用するには、ポリシーをソース管理に保存し、デベロッパーがそれを同期して各自のプロキシ開発環境で使用できるようにします。

  • DRY(「繰り返しを避けること」)を実践するには、できる限り、再利用可能な専用の関数をポリシーの構成やスクリプトに実装します。たとえば、リクエスト メッセージからクエリ パラメータを抽出する専用のポリシーを ExtractVariables.ExtractRequestParameters とし、CORS ヘッダーを挿入する専用ポリシーを AssignMessage.SetCORSHeaders として作成します。こうしたポリシーをソース管理システムに格納すれば、パラメータの抽出や CORS ヘッダーの設定が必要な各 API プロキシに追加でき、冗長な(ひいては管理性を低下させる)構成を作成する必要がなくなります。
  • 使用しないポリシーやリソース(JavaScript、Java、XSLT など)、特にインポートやデプロイの処理速度を低下させかねない大きなリソースを、API プロキシからクリーンアップします。

命名規則

  • ポリシーの name 属性と XML ポリシー ファイル名を同じにします。
  • Script ポリシーや ServiceCallout ポリシーの name 属性をリソース ファイルの名前と同じにします。
  • DisplayName は、該当する API プロキシの使用経験がまったくない担当者にポリシーの機能が正確に伝わるように指定してください。
  • 機能に応じてポリシーに名前を付けます(例: AssignTargetAuthHeaderAssignMessage.TargetAuthHeaderRaiseFault.InvalidUser)。
  • リソース ファイルには適切な拡張子を指定してください。JavaScript には .js、Python には .py、Java JAR ファイルには .jar を使用します。
  • 変数名は決まったスタイルに統一します。特定のスタイル(キャメルケースやスネークケースなど)を選択したら、API プロキシ全体でそれを採用します。
  • 可能であれば、変数プレフィックスを使用して、目的に基づき変数を整理します(例: Consumer.usernameConsumer.password)。

API プロキシの開発

初期設計での考慮事項

  • RESTful API 設計のガイダンスについては、電子書籍の『Web API 設計: ザ ミッシング リンク』をダウンロードしてください。
  • Apigee Edge のポリシーや機能をできるだけ活用して API プロキシを構築します。プロキシ ロジックをすべて JavaScript、Java、または Python のリソースでコーディングすることは避けてください。
  • Flow は整理して構築します。同じ PreFlow や Postflow に条件を複数添付するよりは、条件が 1 つだけの Flow を複数使用してください。
  • 「フェイルセーフ」として、/ が ProxyEndpoint の BasePath のデフォルトの API プロキシを作成します。これを使用することで、ベース API リクエストをデベロッパー サイトにリダイレクトしたり、カスタム レスポンスを返したり、デフォルトの CLASSIFICATION_ERROR を返すよりも有益な別のアクションを実行したりできます。
  • TargetServer リソースを使用して、TargetEndpoint 構成を具体的な URL から切り離すことで、環境を超えた昇格に対応できるようにします。
    バックエンド サーバー間の負荷分散をご覧ください。
  • RouteRule が複数ある場合は「デフォルト」、つまり無条件の RouteRule として RouteRule を 1 つ作成します。デフォルトの RouteRule が条件付きルートリストの最後に定義されていることを確認してください。RouteRule は ProxyEndpoint でトップダウンで評価されます。
    API プロキシ構成リファレンスをご覧ください。
  • API プロキシのバンドルサイズ: API プロキシ バンドルが 15 MB を超えないようにしてください。Apigee Edge for Private Cloud では、thrift_framed_transport_size_in_mb プロパティの変更によってサイズ制限を変更できます。このプロパティは、cassandra.yaml(Cassandra 内)と conf/apigee/management-server/repository.properties にあります。
  • API のバージョニング: API のバージョニングに関する Apigee の考え方と推奨事項については、電子書籍の『Web API 設計: ザ ミッシング リンク』バージョニングをご覧ください。

CORS の有効化

API を公開する前に、API プロキシで CORS を有効にして、クライアント側のクロスオリジン リクエストをサポートする必要があります。

CORS(クロスオリジン リソース シェアリング)は、ウェブページで実行される JavaScript XMLHttpRequest(XHR)呼び出しがオリジン ドメイン以外のリソースとやり取りできるようにする、標準メカニズムです。CORS は、あらゆるブラウザで強制されている同一オリジン ポリシーに対して一般的に実装されている対策です。たとえば、ブラウザで実行中の JavaScript コードから Twitter API への XHR 呼び出しは失敗します。その理由は、ブラウザに該当ページをサービスしているドメインが、Twitter API をサービスしているドメインと同じでないからです。CORS はこの問題を解消するため、サーバーがクロスオリジンでリソースを共有したい場合に「オプトイン」できるようにします。

API を公開する前に API プロキシで CORS を有効にする方法の詳細については、API プロキシへの CORS サポートの追加をご覧ください。

メッセージ ペイロード サイズ

Edge でメモリの問題が起きないようにするため、メッセージ ペイロード サイズは 10 MB に制限されています。このサイズを超えると、protocol.http.TooBigBody エラーが発生します。

この問題については、こちらの Apigee コミュニティの投稿でも取り上げられています。

Edge で大きなメッセージ サイズを扱う場合の推奨事項を以下に示します。

  • リクエストとレスポンスをストリーミングします。ストリーミングすると、ポリシーがメッセージのコンテンツにアクセスできなくなります。リクエストとレスポンスのストリーミングをご覧ください。
  • Edge for Private Cloud 4.15.07 以前のバージョンでは、Message Processor の http.properties ファイルを編集して、HTTPResponse.body.buffer.limit パラメータの限度を大きくします。本番環境にデプロイする前に変更内容を必ずテストしてください。
  • Edge for Private Cloud 4.16.01 以降のバージョンでは、ペイロードを含むリクエストに Content-Length ヘッダーが含まれているか、「Transfer-Encoding: chunked」ヘッダーがストリーミングされている必要があります。ペイロードが空の API プロキシへの POST の場合は、Content-Length を 0 にする必要があります。
  • Edge for Private Cloud 4.16.01 以降のバージョンでは、/opt/apigee/router.properties または message-processor.properties で次のプロパティを設定して制限を変更します。詳細については、Router または Message Processor のメッセージ サイズ制限を設定するをご覧ください。

    どちらのプロパティも、10 MB に対応するデフォルト値は「10m」です。
    • conf_http_HTTPRequest.body.buffer.limit
    • conf_http_HTTPResponse.body.buffer.limit

障害処理

  • すべての障害処理に FaultRules を使用します(RaiseFault ポリシーを使用して、メッセージ Flow を止めて処理を FaultRules Flow に渡します)。
  • FaultRules Flow 内では、障害レスポンスの構築に RaiseFault ポリシーではなく AssignMessage ポリシーを使用します。発生した障害の種類に応じて、AssignMessage ポリシーを条件実行してください。
  • デフォルトの「キャッチオール」障害ハンドラを必ず含めて、システムが生成する障害を顧客定義の障害レスポンス形式にマッピングできるようにしてください。
  • 可能であれば、企業やプロジェクトで使用できる標準形式に沿って障害レスポンスを作成します。
  • エラー状況への解決策を指示するような、意味のある判読可能なエラー メッセージを使用します。

障害の処理をご覧ください。

業界のベスト プラクティスについては、RESTful エラー レスポンス設計をご覧ください。

永続性

Key-Value マップ

  • Key-Value マップはデータセットに限定して使用します。長期的なデータ保存用ではありません。
  • Key-Value マップは Cassandra データベースに格納される情報なので、使用に際してはパフォーマンスを考慮してください。

Key-Value Map Operations ポリシーをご覧ください。

レスポンスのキャッシュ処理

  • レスポンスに成功しなかった場合、またはリクエストが GET ではなかった場合は、レスポンス キャッシュへの挿入はしないでください。作成、更新、削除はキャッシュしません。 <SkipCachePopulation>response.status.code != 200 or request.verb != "GET"</SkipCachePopulation>
  • キャッシュには決まった 1 種類のコンテンツ(XML や JSON など)を挿入します。responseCache エントリを取得したら、JSONtoXML や XMLToJSON で必要な種類のコンテンツに変換します。これにより、データの二重、三重などの格納を避けられます。
  • キャッシュキーがキャッシュ要件を満たしていることを確認します。多くの場合、request.querystring を一意識別子として使用できます。
  • 明らかな必要性がない限り、API キー(client_id)をキャッシュキーに含めないでください。一般に、キーだけでセキュリティ保護されている API は、与えられたリクエストに対し、すべてのクライアントに同じデータを返します。API キーに基づいて多数のエントリに同じ値を格納するのは非効率的です。
  • 適切なキャッシュ有効期間を設けて、ダーティリードを防ぎます。
  • 可能であれば、キャッシュへの挿入に関するレスポンス キャッシュ ポリシーが、ProxyEndpoint レスポンス PostFlow のできるだけ遅い段階で実行されるようにしてください。言い換えると、JavaScript ベースのメディエーションや、JSON と XML の間の変換など、変換手順やメディエーション手順の後に実行します。メディエーション後のデータをキャッシュに保存することで、キャッシュ データを取得するたびにメディエーション手順を実行するというパフォーマンス コストを回避できます。

    メディエーションの結果がリクエストに応じて異なる場合は、メディエーションされていないデータをキャッシュに保存することもできます。

  • キャッシュ エントリをルックアップするレスポンス キャッシュ ポリシーを、ProxyEndpoint リクエスト PreFlow で機能するようにしてください。また、キャッシュキー生成を除き、キャッシュ エントリを返す前にロジックを実装しすぎないようにします。そうしないと、キャッシュに保存するメリットが大幅に減ります。
  • 一般に、レスポンス キャッシュ ルックアップは常にクライアント リクエストにできるだけ近づけます。逆に、レスポンス キャッシュの挿入は、クライアント レスポンスにできるだけ近づけます。
  • プロキシで複数の異なるレスポンス キャッシュ ポリシーを使用する場合は、以下のガイドラインに従って、それぞれの個別の動作を確認してください。
    • 相互に排他的な条件に基づいて各ポリシーを実行します。このようにすると、複数のレスポンス キャッシュ ポリシーのうち 1 つのみが確実に実行されるようになります。
    • レスポンス キャッシュ ポリシーごとに異なるキャッシュ リソースを定義します。ポリシーの <CacheResource> 要素でキャッシュ リソースを指定します。

Response Cache ポリシーをご覧ください。

ポリシーとカスタムコード

ポリシーか、カスタムコードか

  • (できる限り)組み込みポリシーを優先して使用します。Apigee ポリシーは堅実で、最適化されており、サポート対象です。たとえば、ペイロードの作成やペイロードからの情報の抽出(XPath、JSONPath)などには、(できる限り)JavaScript ではなく標準の AssignMessage ポリシーや ExtractVariables ポリシーを使用してください。
  • JavaScript は Python や Java より好まれています。ただし、パフォーマンスが最優先であれば、JavaScript ではなく Java を使用してください。

JavaScript

  • JavaScript は、Apigee ポリシーより直感的な場合に使用します(たとえば、target.url を多数の異なる URI の組み合わせで設定する場合)。
  • JSON オブジェクトを介したイテレーションや Base64 エンコード / デコードなど、複雑なペイロード解析に使用します。
  • JavaScript ポリシーには時間制限があり、無限ループはブロックされます。
  • 必ず JavaScript Step を使用し、ファイルを jsc リソース フォルダに配置してください。JavaScript ポリシー型がデプロイ時にコードをプリコンパイルします。

JavaScript による API プロキシのプログラミングをご覧ください。

Java

  • パフォーマンスが最優先の場合、またはロジックを JavaScript では実装できない場合は、Java を使用します。
  • Java ソースファイルをソースコード追跡の対象に含めます。

API プロキシで Java を使用する方法については、Java コールアウトを使用してレスポンスを大文字に変換するJava Callout ポリシーをご覧ください。

Python

  • どうしても必要な場合を除いて、Python は使用しないでください。Python スクリプトは実行時に解釈されるので、システム実行にパフォーマンス ボトルネックが生じかねません。

スクリプト コールアウト(Java、JavaScript、Python)

  • グローバルの try/catch またはそれに相当する機能を使用します。
  • 意味のある例外をスローし、適切にキャッチして、障害レスポンスに使用します。
  • 例外は早めにスローおよびキャッチします。グローバルの try/catch をあらゆる例外の処理に使用することは避けてください。
  • 必要に応じて、null や未定義値をチェックしてください。たとえば、省略可能なフロー変数の取得時にチェックします。
  • スクリプト コールアウト内で HTTP/S リクエストを作成しないようにします。代わりに、接続を円滑に処理する Apigee ServiceCallout ポリシーを使用してください。

JavaScript

  • API プラットフォーム上の JavaScript は、E4X 経由で XML をサポートしています。

JavaScript オブジェクト モデルをご覧ください。

Java

  • メッセージ ペイロードにアクセスする場合は、context.getMessage()context.getResponseMessagecontext.getRequestMessage のいずれかを使用してみてください。これにより、リクエスト フローでもレスポンス フローでもペイロードを取得できます。
  • ライブラリは Apigee Edge の組織または環境に読み込み、JAR ファイルには含めないでください。これによりバンドルサイズが小さくなり、他の JAR ファイルが同じライブラリ リポジトリにアクセスできるようになります。
  • JAR ファイルは、Apigee リソース API を使用して読み込み、API プロキシ リソース フォルダには含めません。これにより、デプロイにかかる時間が短縮され、同じ JAR ファイルを複数の API プロキシが参照できるようになります。他にも、クラスローダーを分離できるという利点があります。
  • Java をリソース処理(スレッドプールの作成と管理など)に使用しないでください。

Java コールアウトを使用してレスポンスを大文字に変換するをご覧ください。

Python

  • 意味のある例外をスローし、適切にキャッチして、Apigee 障害レスポンスで使用してください。

Python Script ポリシーをご覧ください。

ServiceCallout

  • プロキシ チェーンの使用については、参考になる使用事例が多数あります。プロキシ チェーンでは、ある API プロキシのサービス コールアウトを使用して別の API プロキシを呼び出します。プロキシ チェーンを使用する場合は、同じ API プロキシに戻ってくる「無限ループ」の再帰コールアウトを確実に避けてください。

    同じ組織と環境にあるプロキシ間を接続する場合は、必ずチェーンによる API プロキシの接続をご覧になり、不必要なネットワーク オーバーヘッドを避けるローカル接続の実装について確認してください。

  • ServiceCallout リクエスト メッセージを AssignMessage ポリシーを使用して構築し、リクエスト オブジェクトをメッセージ変数に代入します(リクエスト ペイロード、パス、メソッドの設定など)。
  • ポリシー内で構成される URL には、プロトコル指定が必要です。つまり、URL のプロトコル部分、たとえば、https:// は変数では指定できません。また、URL のドメイン部分や URL の残り部分にも、それぞれ変数を使用する必要があります(例: https://{domain}/{path})。
  • ServiceCallout のレスポンス オブジェクトは別個のメッセージ変数に格納します。そのうえでメッセージ変数を解析し、元のメッセージ ペイロードは他のポリシーでの使用に備えて変更なしで残します。

Service Callout ポリシーをご覧ください。

エンティティへのアクセス

AccessEntity ポリシー

  • パフォーマンスを向上させるには、アプリのルックアップにアプリ名ではなく uuid を使用します。

Access Entity ポリシーをご覧ください。

ロギング

  • バンドル間や同一バンドル内では、一般的な syslog ポリシーを使用します。これにより、ログ記録形式に一貫性がもたらされます。

Message Logging ポリシーをご覧ください。

モニタリング

クラウドのお客様は、Apigee Edge の個々のコンポーネント(Router、Message Processor など)をチェックする必要はありません。Apigee のグローバル オペレーション チームが、あらゆるコンポーネントを徹底的にモニタリングしています。お客様から正常性チェックのご要望があれば、API の正常性チェックも実施します。

Apigee Analytics

分析では、エラー率が測定されることから、非クリティカルな API モニタリングを提供できます。

分析ダッシュボードをご覧ください。

トレース

API Edge 管理 UI のトレースツールは、API の開発中や本番環境での実行中に発生する API の問題をデバッグするのに便利です。

トレースツールの使用をご覧ください。

セキュリティ