外部パートナーとのAPI連携時に気をつけるポイント

はじめに

こんにちは!yamakazu (@yamarkz) です。

近所の行きつけスーパーがサミットストアになったのですが、品揃えがとても良く、お店の雰囲気も明るくて、仕事終わりの買い物が最近の楽しみになってます 🥳 🛒🥗

さて今回は、開発方面のナレッジとして外部API連携の話を紹介します。非常にニッチな領域の話題ですが、わかる人にはわかるような内容です。

興味のある方はぜひ最後まで読んでみてください。

動機

新しく外部API連携の開発に着手するメンバーの助けになりたい、より良い外部API連携を実現したいという思いから、これまで開発を経験してきた中で理解した勘所を紹介します。

元々は社内向けに書き溜めておいたナレッジメモの内容ですが、特別社内に留めておく必要性もないので、せっかくならブログにしてしまおうと思い、ここで筆を取りました。

これは社内の同僚に向けた内容でありながら、似た境遇を持つ社外の方向けへの内容でもあります。

書き振りは開発を始める前に読む or 開発を進めながら読む開発ガイドのような立ち位置を目指して書きました。

先に全体を通して言いたいことを述べておくと、

「悪いことは言わないから、脇は締めれるだけ締めておけ…」です。はい

ここでの内容が、より良い開発の助けになれれば幸いです。

前提

WebAPIを用いた連携を想定

システム連携はWebAPI (HTTP Request) を用いて自分達が連携先にリクエストを送るケースで考えます。 WebhookやCSVファイルをストレージにアップロードするといったケースではありません。

WebAPIは公開範囲が限定的なAPIを想定

WebAPIがどういったユースケースを想定して作られているかによって、考慮は変わります。

ここでは公開範囲が限定的で、双方に認識が取れる関係性での連携を想定します。10Xではこれを「パートナーAPI」と呼んでいます。

参考として、API連携の公開範囲による分類は以下と定義します。

  • プライベートAPI
    • 自分たちが管理するサービス間で通信を行うAPIをプライベートAPIと呼ぶ
  • パートナーAPI
    • 双方に認識が取れる関係で通信を行うAPIをパートナーAPIと呼ぶ
  • パブリックAPI
    • 不特定多数の認識が取れない関係で通信を行うAPIをパブリックAPIと呼ぶ

気をつけるポイント

1. 仕様書が最新であり、内容に漏れ、ダブり、誤字脱字がないことを確認する

仕様書は開発前に細かく確認しましょう。📄

API連携において仕様書は資産です。開発者の拠り所とも言えます。

希望としては提供してもらう仕様書には高い品質を要求したいです。しかし、利用するAPIがパブリックAPIでない場合、(経験上) 仕様書の整備が甘いケースが多いです。

提供してもらった時点で必ず内容自体が最新であるか、漏れやダブりがないか、誤字や脱字がないかを確認しましょう。

そんなことから!?と思ってしまうかもしれませんが、ここが誤っている時点で将来的な時間のロスや混乱を防げます。

仕様書と実態の実装が違うことで、使い方が間違っているのか、現状が正しいのかわからず確認作業に時間を溶かすといったことが容易に起きえます。

たぶんなんとかなるっしょ!と思わず (過去の自分がそう…) 、正確に仕様把握できる情報であることに拘る姿勢を推奨します。

(余談: 整備が甘い理由は利用者が限定的で変更頻度が低く、メンテナンス動機が湧かないからなのではないかと推察しています)

2. 検証環境を用意してもらう

検証環境は必ず用意してもらいましょう。🧪

これまで用意されなかった経験はありませんが、ない状態、つまり開発環境での結合テストなしで本番リリースは考えられません。🙅‍♂️

可能な限り本番と同じ挙動の検証環境が望ましいですが、一定許容しなければいけないケースもあります。その場合でも、きちんと開発でテストできる状態は作り、テストを通した上でリリースに向かいましょう。

3. API連携の骨子を確認する

詳細把握に進む前に、根幹となるAPIの基礎要件を把握しましょう。👷

大体が仕様書の最上部にまとまっているはずです。

以下の要点を押さえておけばOK。

  • 通信方式
  • リクエストフォーマット / レスポンスフォーマット
  • リクエストヘッダー (Content-Typeやアクセストークン)
  • 操作制約 (時間制約, ネットワーク制約, リクエスト制約)
  • ステータスコード (HTTP固有, 独自定義)
  • エンドポイント / URLパス
  • 文字コード
  • 認証方式 / 認可方式

4. フィールドの表現と値の意味を確認する

各リクエスト/レスポンスの中身を精査し、フィールドの表現と値の意味を把握しましょう。🔵

全体の輪郭を掴みながら読むイメージで、少しでも疑問に思う点があれば細かく質問します。

以下の要点を押さえておけばOK。

  • フィールドは必須なのか任意なのか
  • 必須でも空文字である場合、何らかの意味判定をするのか
  • フィールドがない場合の扱いをどう解釈するのか
  • 共通して要求されるフィールドがあるのか/ないのか
  • 数字の表現に特殊な法則はないのか
  • 文字数制限を設けている意図は何か、超えた場合どうなるのか
  • 配列である場合、件数制限はあるのか

上記に挙げたのは一例で、他にもこれはどういう意味なのか? この例外ケースの場合はどうなるのか?と仕様書を読み解く時点で気になったケースの疑問は全て明らかにしておきます。

この注意により、作り手の意図を把握し、正しい使い方ができるようになります。(= バグの混入や不具合を防ぐ)

5. エラーの表現と意味を確認する

エラーケースも把握しましょう。🚷

これまでエラー表現方面では特別に躓いたことはなく、無難にやれば良いです。

以下の要点を押さえておけばOK。

  • エラーレスポンスのフォーマット
  • エラーのステータスコードとステータスの意味
  • 独自のエラー解釈
{
  "result_code": 300, // 独自ケースを考慮する
  "result_type": "invalid_parameters",
  "result_message": "無効なパラメータです"
}

6. リソースとフィールドのライフサイクルを明らかにする

リソースとフィールドのライフサイクルを把握することで、APIの理解を深めましょう。🌀

リソースのライフサイクルというのは、リソース自体の作成、更新、削除といった状態遷移のこと。

フィールドのライフサイクルというのは、status といったフィールドで表現されるもので (これは鉄板ネタ) 、何らかの申請を行う場合には、申請前, 申請完了, 審査中, 承認, 非承認といった状態遷移が考えられます。

経験上、大凡の仕様書にはリソースに対する操作方法は明記されますが、自分達の操作以外で変わりうるケースが明記されていない場合があります。その場合、リソースを扱う側としては、変更をどのように解釈すれば良いのかわからなければ、適切な振る舞いができません。もちろん、自分達の操作でのみ変わるというケースもあるため、その場合は考慮が減ります。

ライフサイクルにおけるWhy/What/When/Whoを把握しておきましょう。

フィールドに関してよくある例は、キャンセルというステータスでキャンセル前に戻したい場合、どの操作を受け付けるのか?というもの。

“キャンセル取り消し”というAPIを操作するのか? ”ステータス更新”のAPIでキャンセル前のステータスに上書きするのか?などが考えられます。この答えはケースによる。

ステータスの意味解釈は明記されていると思いますが、それがいつ, 何を起因に, どれに変わるのか, そこに制約はあるのか を把握するよう意識しましょう。

7. リソース操作 (作成 / 更新 / 削除) のAPIがある場合、操作後の結果 (リソース or 操作履歴 or リクエストログ) が確認できる参照APIを用意してもらう

参照APIは可能な限り用意してもらいましょう。🔍︎

パブリックAPIを利用した経験がある方からすると、操作はできるのに参照できないケースとかあるの???と思われるかもしれません。そういう世界もあるんです。

経験例でいくと、初期に提示してもらったAPIに参照系がなかったので、開発を依頼して後ほど追加してもらいました。参照APIを必要とする理由は、結果整合性の担保です。

クライアント側で整合性を確認するには参照APIがなければできません。厳密にはレスポンスなどから推論もできますが大変です。

参照APIで得た結果を元に、自律的に整合性を取り直す動き(リコンサイル)ができる状態を作ることで、確実にビジネス要求に応えられるようにします。

参照対象はケースによって変わります。リソースそのものの場合もあれば、計算を必要とする数字である場合は計算操作履歴であったりもします、もしくはリクエストログそのもの。

この辺りはユースケースに応じた、扱いやすい参照APIを用意してもらいましょう。

8. リソース操作 (作成 / 更新 / 削除) のAPIで冪等性を担保する仕組みを確認する

リソースによっては操作で冪等性を持ちたい場合があります。冪等性が担保された操作が可能であるかを確認しましょう。🔘

例えば、特定のお客様に何らかの特典を付与するという場合に付与リクエストは通ったものの、レスポンス時にネットワークエラーが発生し、エラーだとみなして付与リクエストをリトライしたとします、この時エラーになったからといって安易にリトライしてしまうと、仮にAPIに冪等性がない場合、重複して特典を付与してしまうことになります。

特典付与であるため、お客様の不利益にはなってないから良いという判断ができるかもしれませんが、これが権利の付与ではなく権利の消費である場合は不利益につながります。🥶

こういったクリティカルなケースに対応するためにも冪等性を持った処理、API仕様は必要であり重要です。

今でこそHTTPの標準仕様としてIdempotency HTTP Headerが検討されていますが、これはまだ最近の話で提案仕様自体ドラフトでもあるため、近しい形でサポートされている方が珍しいでしょう。ちなみに、AmazonやPaypalといったBigTech系は標準提案に近い形でサポートしています。

もし何らかの仕組みがサポートされていれば、その仕組みに従いましょう。

そうでない場合はクライアント、つまり自分達のリクエスト処理で制御する必要があります。この時に役立つのが、7で取り上げた参照APIです。

複雑なリクエスト処理になってしまいますが、リトライ時に参照APIでサーバー側の状況を確認した後にリトライ可否を判断することで処理として冪等性を担保します。

冪等性の担保をAPIの仕様に頼るのではなく、処理自体でなんとかする方針です。

Future<void> main() async {
  final apiClient = ApiClient();
  RetryOptions().retry(() async {
    // 処理のサイクルで事前チェックを挟み、挙動を制御する
    final notConsumed = await checkDiscountTicketConsumed(request);
    if (notConsumed) {
      // notConsumedであることがわかった場合にのみAPIリクエストを行う
      await apiClient.consumeDiscountTicket(request);
    } else {
      logger.info('already processed requestId: ${request.id}');
    }
  }, onRetry: (error) {
    logger.info('start retry reason: ${error.toString()}');
  });
}

9. 障害発生時のリカバリーとフォールバック方法を確認する

障害発生時のリカバリーとフォールバック対応の考慮もしましょう。🧯

ここでの障害発生は自分達側の場合もあれば、連携先側の場合もあります。

これはプレモーテムとして事前に想定して回復性と耐障害性を担保しましょうという話。

考えにくい例ですが、決済の様なAPI連携が同期的で外せない連携である場合、連携先が障害の場合は自分達の処理も止める可能性があります。

対して、特典付与といった非同期で完結する処理である場合、時間をおいて手動で対応すれば解決する場合もあります。

障害発生時の対応は文脈とユースケースでリカバリー方法は定まるため、一概にこうするべきという汎用的なプラクティスは提示できないのですが、以下の問いに答えを出しておくと良いかもしれません。

  • 障害発生時にどの様に振る舞うのか
  • システムが復旧したらいつから正常処理を再開するのか
  • 何を基準に処理再開を判断して良いのか
  • 再開前に整合性を確認する必要があるのか
  • 双方のシステムで処理を経た結果、最終的なあるべき状態は何か

こういった問いの答えから冪等性の担保やリトライ、ロールバック、フォールバック機構、リコンサイルバッチ、チェッカーなどの必要箇所が自然と見えてくるはずです。

事前の障害対策、予防策こそがシステム設計における逆算性を発揮するポイントです。

10. 有事に備えてパートナーとコミュニケーションパスを確立する

有事に備えてパートナーとコミュニケーションパスを確立しましょう。🗣️

平時でも密にコミュニケーションを取る機会があれば、気にする必要はありませんが、1度実装してしまい、運用に入れば特段コミュニケーションを取る必要がないという場合もあります。

その場合、突発的に障害が発生すると普段コミュニケーションが起きていない分、情報共有の初動が遅れるリスクが生まれます。

障害発生頻度が稀であったとしても、コミュニケーションパスを確立しておいて損はないため、立ち上げタイミングでパスを確立しておくことを勧めます。

具体的には、問題発生時に誰にどの手段で連絡を入れるのか、時間帯的にいつ頃に連絡するのがよく、社内外で誰が表に立ってやりとりするのかを双方認識を揃えておきます。

10Xでは、緊急度が低いが重要度が高く、情報共有の密度が高いものはNotionのページをシェアしてやりとりする場合があったり。緊急度が高く重要度も高い内容は メール or BizDevのメンバーがパートナー側の責任者と即時に連絡が取れるように電話対応が可能な体制を築いていたりします。

この辺りはアーキテクチャ特性のクリティカル度合いであったり、障害発生時のインパクトなどからグラデーションを持たせて整備しても良いかもしれません。

まとめ

外部API連携時に気をつけるポイント

  1. 仕様書が最新であり、内容に漏れ、ダブり、誤字脱字がないことを確認する
  2. 検証環境を用意してもらう
  3. API連携の骨子を確認する
  4. フィールドの表現と値の意味を確認する
  5. エラーの表現と意味を確認する
  6. リソースとフィールドのライフサイクルを明らかにする
  7. リソース操作 (作成 / 更新) のAPIがある場合、操作後の結果 (リソース or 操作履歴 or リクエストログ) が確認できる参照APIを用意してもらう
  8. リソース操作 (作成 / 更新) のAPIで冪等性を担保する仕組みを確認する
  9. 障害発生時のリカバリーとフォールバック方法を確認する
  10. 有事に備えてパートナーとのコミュニケーションパスを確立する

ここまでのポイントを抑えて、脇は締めれるだけ締めて開発に臨みましょう

最後に

外部API連携で気をつけるポイントを基本的なところから、特異なところまで紹介してきました。

パートナーAPIはプライベートほど柔軟ではなく、パブリックほど整備されていないという、絶妙な曖昧さを持ち、普段以上に注意して進めなければ良い開発ができません。

ここでの内容が、外部API連携に携わる方の助けになれれば幸いです。

10XではリッチなUI/UXの開発から、現場にDeep Diveした泥臭い価値検証はもちろん、最近は堅牢で拡張性の富んだシステムを目指したリアーキテクチャの取り組みも始まっています。

10x.co.jp

今回取り上げたような外部API連携に関わる機会はたくさんあり、ぜひ腕を振いたい!難しい問題を解いてインパクトを残したい!という方には良い機会を提供できる自信があります。

少しでも興味を持っていただけましたら、ぜひ採用ページを覗いてみてください。