Apigee を使用した API プロキシの設計と開発のベスト プラクティス

このページの内容は ApigeeApigee ハイブリッドに該当します。

Apigee Edge のドキュメントを表示する。

このドキュメントでは、Apigee を使用して API プロキシを開発するための一連のベスト プラクティスについて説明します。

ここでは、設計、コーディング、ポリシーの使用、モニタリング、デバッグについて取り上げます。記載されている情報は、Apigee を利用して API プログラムを実装し、成功を収めたデベロッパーの経験から得られたものです。このドキュメントの内容は随時更新されます。

ここに示すガイドラインに加えて、アンチパターンの概要も役に立ちます。

開発基準

コメントとドキュメント

  • ProxyEndpointTargetEndpoint の構成にインライン コメントを残してください。特に、ポリシー ファイル名で Flow の基本的な機能がはっきり示されていない場合は、コメントによって Flow のわかりやすさが向上します。
  • 役に立つコメントを残し、自明なコメントは避けてください。
  • インデント、スペースの挿入、縦方向の配置などに一貫性をもたせます。

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

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

  • DRY(Don't Repeat Yourself: 繰返しを避ける)の原則を実践するため、できる限り、再利用可能な専用の関数をポリシー構成やスクリプトに実装します。たとえば、リクエスト メッセージからクエリ パラメータを抽出する専用のポリシーを ExtractVariables.ExtractRequestParameters とします。
  • 未使用のポリシーやリソース(JavaScript、Java、XSLT など)、特にインポートやデプロイの処理速度が低下する可能性がある大規模なリソースを API プロキシからクリーンアップします。

命名規則

  • ポリシーの name 属性と XML ポリシー ファイル名を同じにする必要があります。
  • Script ポリシーと ServiceCallout ポリシーname 属性、リソース ファイルの名前は同じである必要があります。
  • 該当する API プロキシの使用経験がまったくない担当者が理解できるように、DisplayName でポリシーの機能を正確に表す必要があります。
  • ポリシーには機能に応じた名前を付けます。一貫したポリシーの命名規則を定めることをおすすめします。たとえば、短い接頭辞の後に、ダッシュで区切った一連のわかりやすい単語を続けます(例: AssignMessage ポリシーの名前を AM-xxx にします)。apigeelint ツールもご覧ください。
  • リソース ファイルに適切な拡張子(JavaScript ファイルの場合は .js、Python ファイルの場合には .py、Java JAR ファイルの場合には .jar)を使用します。
  • 変数名は一貫したスタイルにする必要があります。camelCase や under_score など、特定のスタイルを選択した場合は、API プロキシ全体でそれを使用します。
  • 目的に基づいて変数を整理するため、可能であれば変数接頭辞を使用します(例: Consumer.usernameConsumer.password)。

API プロキシの開発

初期設計での考慮事項

  • RESTful API 設計のガイダンスについては、電子書籍の『Web API Design: The Missing Link』をダウンロードしてください。
  • API プロキシを構築する際は、できる限り Apigee のポリシーや機能を活用してください。JavaScript、Java、または Python のリソースですべてのプロキシ ロジックをコーディングすることは避けてください。
  • Flow は整理して構築します。同じ PreFlow や Postflow に複数の条件を付けるのではなく、条件が 1 つの Flow を複数使用してください。
  • フェイルセーフとして、ProxyEndpoint の BasePath を / に設定してデフォルトの API プロキシを作成します。これにより、ベース API リクエストをデベロッパー サイトにリダイレクトしたり、カスタム レスポンスを返したりできます。また、デフォルトの messaging.adaptors.http.flow.ApplicationNotFound を返すよりも有用なアクションを実行できます。
  • 最適なパフォーマンスを得るため、1 つの Apigee 環境または環境グループで使用する API プロキシ ベースパスは 3,000 個以下にすることを推奨します。この推奨値を超えると、新規および既存のすべての API プロキシのデプロイでレイテンシが増加する可能性があります。
  • TargetServer リソースを使用して TargetEndpoint 構成を具体的な URL から切り離し、複数の環境で利用しやすくします。
    バックエンド サーバー間のロード バランシングをご覧ください。
  • RouteRule が複数ある場合は、その 1 つを「デフォルト」(条件が設定されていない RouteRule)として作成します。デフォルトの RouteRule は条件付きルートのリストの最後に定義してください。RouteRule は ProxyEndpoint で上から順に評価されます。API プロキシ構成リファレンスをご覧ください。
  • API プロキシ バンドルのサイズ: API プロキシ バンドルは 15 MB より大きくすることができません。
  • API のバージョニング: API のバージョニングに関する Apigee の考えと推奨事項については、電子書籍『Web API Design: The Missing Link』の「Versioning」をご覧ください。

CORS の有効化

クライアント側のクロスオリジン リクエストをサポートするため、API を公開する前に CORS ポリシーを ProxyEndpoint のリクエスト PreFlow に追加する必要があります。

CORS(クロスオリジン リソース シェアリング)は、ウェブページで実行される JavaScript XMLHttpRequest(XHR)呼び出しがオリジン以外のドメインのリソースとやり取りできるようにする標準メカニズムです。CORS は、すべてのブラウザで適用されている同一オリジン ポリシーへの対策として一般に実装されているソリューションの一つです。たとえば、ブラウザで実行中の JavaScript コードから Twitter API への XHR 呼び出しを行うと、呼び出しが失敗します。これは、ブラウザに該当ページを提供しているドメインが、Twitter API を提供しているドメインと異なるためです。この問題は CORS によって解決でき、クロスオリジン リソース シェアリングを提供したいサーバーは CORS に「オプトイン」できます。

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

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

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

この問題は、Apigee プロキシで大きなペイロードをリクエストまたは返す際のエラーでも説明されています。

Apigee で大きなメッセージ サイズを扱う場合に推奨する方法は次のとおりです。

  • リクエストとレスポンスをストリーミングします。ストリーミング時にはポリシーがメッセージのコンテンツにアクセスできなくなることに注意してください。リクエストとレスポンスのストリーミングをご覧ください。

障害の処理

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

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

永続性

Key-Value マップ

  • Key-Value マップは限られたデータセットのみに使用します。長期的なデータストアのためには設計されていません。
  • Key-Value マップの情報は Cassandra データベースに保存されるため、使用の際はパフォーマンスを考慮してください。

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

レスポンスのキャッシュ保存

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

    ただし、メディエーションの結果がリクエストごとに異なる場合は、メディエーションされていないデータをキャッシュに保存することをおすすめします。

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

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

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

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

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

JavaScript

  • JavaScript は、Apigee ポリシーより直感的に使用できる場合に使います(たとえば、さまざまな URI の組み合わせで target.url を設定する場合など)。
  • JSON オブジェクトの反復処理や Base64 エンコード / デコードなどの複雑なペイロード解析。
  • JavaScript ポリシーには時間制限があるため、無限ループはブロックされます。
  • JavaScript ステップの使用とファイルの配置は、常に jsc リソース フォルダで行います。JavaScript ポリシーでは、デプロイ時にコードがプリコンパイルされます。

Java

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

API プロキシでの Java の使用については、JavaCallout ポリシーをご覧ください。

Python

  • どうしても必要な場合を除いて、Python は使用しないでください。Python スクリプトは実行時に解釈されるため、実行するだけでパフォーマンスのボトルネックが発生することがあります。

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

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

JavaScript

  • API プラットフォーム上の JavaScript では、E4X によって XML がサポートされます。

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

Java

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

Python

  • 意味のある例外をスローし、Apigee 障害レスポンスで使用できるように適切にキャッチします。

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

ServiceCallout

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

    同じ組織と環境内にある複数のプロキシを接続する場合は、不要なネットワーク オーバーヘッドを回避するローカル接続の実装方法について、チェーンによる API プロキシの接続をご覧ください。

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

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

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

AccessEntity ポリシー

  • パフォーマンスを向上させるため、アプリ名ではなく uuid でアプリを検索します。

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

ロギング

  • バンドル間、同じバンドル内で共通の Syslog ポリシーを使用します。これにより、一貫したロギング形式が維持されます。

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

モニタリング

Cloud のお客様は、Apigee の個々のコンポーネント(Router、Message Processor など)を確認する必要はありません。Apigee のグローバル オペレーション チームが、すべてのコンポーネント、API ヘルスチェック、お客様からのヘルスチェック リクエストを徹底的にモニタリングしています。

Apigee Analytics

Analytics では、エラーの割合が測定されるため、非クリティカルな API モニタリングを行えます。

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

Debug

Apigee UI のこのトレースツールは、開発環境または本番環境での API の運用中に API のランタイム問題をデバッグするのに役立ちます。

デバッグの使用をご覧ください。

セキュリティ

API プロキシのカスタム ロジック

API プロキシの構築時によくある要件の一つは、リクエストやレスポンスを処理するためのロジックを組み込むことです。要件の多くは、事前に定義された一連のステップ / アクション / ポリシー(トークンの検証、割り当ての適用、キャッシュ オブジェクトによる応答など)によって満たすことができますが、プログラミングが必要になることもあります。たとえば、リクエスト内のキーに基づいてルーティング テーブルからロケーション(エンドポイント)を検索し、ターゲット エンドポイントやカスタム / 独自の認証方法を動的に適用する場合などです。

Apigee では、デベロッパーがこのようなカスタム ロジックを処理するためのオプションが複数用意されています。このドキュメントでは、各オプションとその使用タイミングについて説明します。

ポリシー ポリシーのユースケース
JavaScript と PythonScript

使用する状況:

  • JavaScript ポリシーと PythonScript ポリシーで行えることは同じです。デベロッパーには、使い慣れているほうの言語を選ぶ傾向があります。
  • JavaScript ポリシーと PythonScript ポリシーは、複雑すぎず、サードパーティのライブラリを使用する必要がないロジックをインラインで処理するのに最適です。
  • これらのポリシーは JavaCallout ポリシーほどパフォーマンスが良くありません。

使用しない場合:

  • Apigee の API ゲートウェイはアプリケーション サーバーではありません(node.js のような完全な JavaScript ランタイムも提供されていません)。コールアウトの処理に常に 1 秒以上かかる場合、そのロジックはゲートウェイに適していない可能性が高く、代わりに基盤サービスに含める必要があります。

ベスト プラクティス: JavaScript のほうがパフォーマンスが優れているため、PythonScript よりも JavaScript が推奨されます。

JavaCallout

使用する状況:

  • ロジックをインラインで処理する場合は、パフォーマンスが重要です。
  • 既存の Java ライブラリには多くのロジックが用意されています。

使用しない場合:

  • Apigee の API ゲートウェイはアプリケーション サーバーではなく、Spring、JEE などのフレームワークを読み込むためのものではありません。コールアウトの処理に常に 1 秒以上かかる場合は、そのロジックを機能ロジック(ビジネス ロジック)と見なすことができます。サービスとして外部化することを検討してください。
  • 不正使用から保護するため、Apigee API ゲートウェイでは実行できるコードのタイプに制限が設けられています。たとえば、特定の暗号ライブラリへのアクセスやファイル システムへのアクセスを試みる Java コードは、実行がブロックされます。
  • 特にサードパーティのライブラリに依存する Java アプリケーションでは、多くの(大きな)JAR ファイルが取り込まれることがあります。これにより、ゲートウェイの起動時間が長くなる可能性があります。
ExternalCallout

使用する状況:

  • カスタム ロジックを外部化し、そのロジックがメッセージ コンテキストにアクセス(また、必要に応じてメッセージ コンテキストを変更)できるようにする用途に適しています。
  • 外部コールアウトは gRPC を実装しており、ServiceCallout よりもパフォーマンスが高くなる可能性があります。
  • Apigee または Apigee ハイブリッドを Google Cloud で使用する場合、このようなロジックは Cloud Functions または Cloud Run でホストすることを検討してください。
  • Apigee Edge のホスト型ターゲット機能に代わる有効な機能です。

使用しない場合:

  • インラインですばやく実行できる軽量のロジックの場合。
ServiceCallout

使用する状況:

  • 複雑なロジックはゲートウェイの外部で実装することをおすすめします。そうすれば、ロジックで独自のライフサイクル(リリースとバージョニング)を設定でき、ゲートウェイの機能も影響を受けません。
  • REST / SOAP / GraphQL エンドポイントがすでに存在するか、簡単に実装できる場合。
  • Apigee または Apigee ハイブリッドを Google Cloud で使用する場合は、Cloud Functions または Cloud Run でこのようなロジックをホストすることを検討してください。
  • Apigee Edge のホスト型ターゲット機能に代わる有効な機能です。

使用しない場合:

  • インラインですばやく実行できる軽量のロジックの場合。
  • API プロキシで、コンテキスト(変数など)を転送するか、外部実装からコンテキストを受け取る必要がある場合。

まとめ

  1. ロジックが単純であるか自明である場合、JavaScript(推奨)または PythonScript を使用します。
  2. インライン ロジックで JavaScript や PythonScript よりも高いパフォーマンスが必要な場合は、JavaCallout を使用します。
  3. ロジックを外部化する必要がある場合は、ExternalCallout を使用します。
  4. 外部実装がすでに存在する場合やデベロッパーがすでに REST に精通している場合は、ServiceCallout を使用します。

次の図に、このプロセスを示します。