Teams アプリから Microsoft Graph を使用するためのベスト プラクティス
はじめに
Teams アプリを開発する上で Microsoft Graph と連携することは欠かせません。Microsoft Graph は Microsoft 365 の統一されたエンドポイントであり、社内のさまざまなリソースにアクセスできます。たとえば、以下のようなシナリオが考えられます。
- チームの同僚のプロフィール画像を表示
- アプリに表示されている内容を自分のタスクとして Planner に追加
- 指定したユーザーと来週のオンライン ミーティングを設定
Teams アプリはチームのコラボレーションや生産性を高める目的で作成されることが多いです。そのため、ユーザビリティを損なうことなくどのようにして Microsoft Graph にアクセスするかは大きな課題です。この記事では、Teams アプリから Microsoft Graph を使用するためのベスト プラクティスおよびいくつかのテクニックについて解説します。
429 (Too Many Requests) HTTP ステータスに対応する
Microsoft Graph に限らず、API サービスはリクエストに対してスロットリングを設けることで、サービスが高負荷になることを防止しています。リクエストがしきい値を超える場合、リクエストは処理されず、429 HTTP ステータスが返されます。
Microsoft Graph におけるスロットリングのしきい値は以下の Microsoft のドキュメントに公開されています。
docs にもある通り、429 HTTP ステータスに対しては エラーに対応する、エラーを回避する という 2 つの方法が必要です。これらはどちらか片方だけでよいというものではなく、どちらも実施する必要があります。
エラーに対応する
429 HTTP ステータスが発生する場合はレスポンスに Retry-After ヘッダーが含まれるため、ヘッダーで指定された秒数だけ待機してリトライするように設計する必要があります。リトライを自前で実装するのは大変なので、Microsoft Graph SDK の利用を検討してください。Microsoft Graph SDK には RetryHandler というミドルウェアがあり、429 HTTP ステータスが発生したときのリトライを代わりに行います。
エラーを回避する
バッチ リクエストについて
バッチ リクエストは Microsoft Graph へのリクエストそのものを減らしてパフォーマンスを向上させるテクニックです。ただし、それぞれのリクエストが個別に評価されるため、429 HTTP ステータスの回避には役立ちません。また、リクエストの一部が 429 HTTP ステータスになった場合でも、全体としては 200 HTTP ステータスを返すことがあるため、Microsoft Graph SDK はリトライを行いません。
レスポンスをキャッシュする
Teams アプリはタブが表示されるたびに新しいページが読み込まれます。ユーザーがタブを切り替えることは頻繁にあるため、そのたびに Microsoft Graph リクエストを送信しているとすぐにスロットリングのしきい値に達してしまいます。取得したリクエストは一定期間キャッシュすることで余計なリクエストを減らし、パフォーマンスの向上にもつながります。キャッシュの場所としては IndexedDB の利用が推奨されます。また、Microsoft Graph Toolkit を使用すると自動的にキャッシュも行われます。
トークンの有効期間に対応する
Microsoft Graph へのリクエストには OAuth によるアクセス トークンが必要です。アクセス トークンの有効期間はアプリケーションの種類や組織のポリシーによって異なり、Microsoft のドキュメントには明示されていませんが、通常は 1 時間から数時間程度です。[1]
Microsoft Teams は仕事をしている間はずっと起動しているため、Teams アプリも長時間起動し続けている可能性があります。アプリの使用中にアクセス トークンの有効期間が切れるシナリオは想定しておく必要があります。
アクセス トークンの期間切れを検知する
Microsoft Graph SDK や Microsoft Graph Toolkit を使用している場合、アクセス トークンを渡すロジックはコールバックになっています。よって、アクセス トークンが要求されるタイミングは開発者が制御できます。このタイミングでアクセス トークンの有効期間が切れているかどうかをチェックする必要があります。Azure AD のアクセス トークンは JWT なのでデコードして中身を検証できます。exp には UNIX 時間 (1970/01/01 からの経過秒数) が含まれるため、これが過去の時間を示す場合には適切なハンドリングを行う必要があります。
アクセス トークンの期間切れに対応する
手動で再表示するようにユーザーに促す
あまりスマートな方法ではありませんが、一時的な回避方法として、エラー画面を表示して再表示させるというのが簡単です。アクセス トークンはアプリの初期化時に取得することが多いため、再表示をさせることで問題を解決できます。
自動的にアクセス トークンを再取得する
可能な限りこの方法を採用してください。アクセス トークンが期限切れになったことを検知したとき、microsoftTeams.authentication.getAuthToken メソッドを呼び出ます。このメソッドでは、新しいクライアント トークンを取得し、サーバー トークンに変換します。getAuthToken メソッドはコールバックを受け取りますが、取り回しが悪いため Promise に変換するヘルパーを作成することをおすすめします。
function getAuthToken() {
return new Promise((resolve, reject) => {
microsoftTeams.authentication.getAuthToken({
successCallback: (token) => resolve(token),
failureCallback: (error) => reject(error)
});
});
}
パフォーマンスを向上させる
Google Chrome では同時に接続できるリクエストの数が 6 に制限されています。Electron で開発されている Microsoft Teams も同様の制限を受けると考えるべきです。大量のリクエストが発生する場合はリクエスト数を減らすことでパフォーマンスを向上できます。すでに述べた 429 HTTP ステータスへの対応と重複する点もあります。
OData クエリを活用する
たとえば、元となるデータとそれに関連するデータを取得するときに、$expand パラメーターを使用することでリクエストの回数を削減できます。また、$select パラメーターを使用することでレスポンス データのサイズを削減し、パフォーマンスを向上できます。
バッチ リクエストを使用する
API の種類によっては $expand
ができないものもあります。たとえば、/teams/{id}
と /teams/{id}/members
はアクセス許可が異なるため、$expand
が使用できません。このような場合にバッチ リクエストを使用することでリクエストを減らせます。注意点として、バッチ リクエストでは依存関係のあるリクエストの順序を指定できますが、実行した結果を別の API のリクエストに含めることはできません。
レスポンスをキャッシュする
データの種類ごとにキャッシュ戦略を立てる必要があります。プロフィール画像のような更新頻度の低いデータと、プレゼンス情報のような更新頻度の高いデータでは、当然キャッシュの有無や有効期間も異なります。
見た目を整える
リクエスト中はユーザーにそうであることを表現する必要があります。Fluent UI には Loader や Skeleton のようなロード中であることを表現するコンポーネントが用意されています。最近は Loader よりも Skeleton が好まれる傾向にあります。いずれにしても、ユーザー体験を損なわないように考慮してください。
おわりに
Teams アプリに限定して書きましたが、ほとんどの内容は Teams アプリ以外にも当てはまります。さらにいえば Microsoft Graph に関係なく API を呼び出すアプリケーション全般に当てはまることも多いです。
- リトライを実装
- リクエストの回数を減らす
- データをキャッシュ
- ユーザー体験を損なわない
このあたりを留意していただければ、価値のある Teams アプリを作成できると考えます。
Discussion