UPSIDER Tech Blog

外部API爆速連携術 ~ 爆速の裏の秘訣 ~

🐘 はじめに

こんにちは!PRESIDENT CARD チームでバックエンドを担当している Take です。

UPSIDERでは日々事業の成長を支えるために他社様のサービスとの連携も必要不可欠です。私が今まで担当してきた機能実装だと下記のようなものがあります。

  • 貯めたポイントをマイルに交換する
  • スマホ上で本人確認を行えるようにする

これらの機能はわずか1ヶ月足らずでリリースすることができました。 なぜそんなに早く進められたのか?それは「外部システムとスムーズに連携するための開発の型」や「詰まりやすいポイントを事前に回避する思考法」をチームで共通化し、再現性のある進め方を意識していたからです。

この記事では、そんな "爆速の裏にある秘訣" を、実際のコード構成やAIツールの活用、整合性担保の工夫なども含めてご紹介します。同じように外部サービスとの連携開発を進める方にとって、少しでも参考になれば幸いです。

🤯 外部連携の難しさとは

なぜ外部連携機能の実装は難しいのでしょうか。私の経験上、大きく分けて下記の3つが原因だと考えています。

仕様把握

外部システムの仕様を読み解くのは想像以上にコストがかかります。こちらが実現したい要件に適したAPIを探し出す必要があるのですが、API自体の数が多かったり、似たような機能のAPIが複数存在していたりと探し出すのは容易ではありません。仕様把握するだけでも多くの時間と労力を要し、大きなハードルの一つとなっています。

実装コスト

SDKやライブラリが提供されていれば比較的簡単に実装できる場合もありますが、そうでない場合はAPI仕様を元に自前で実装する必要があり、それなりの時間と工数がかかります。

データ整合性の担保

1つの処理内で「自社にDB対して処理を行う」「外部システムのAPIを叩く」を実施する場合は多々あります。その場合、どちらか片方の処理が失敗してしまうとデータ不整合が発生してしまいます。

📝 外部連携をスムーズに行うためのTips

これらの課題をどのように解決してるかご紹介します。

🐻 提供された資料は隈なく目をとおす

外部システムの仕様を把握できていないことで期待通りの挙動になってしまうことは多々あります。それを防ぐには提供された資料は隈なく目を通しておくことが大切なのですが、ボリュームが多かったり英語で書かれていたりすると時間がかかってしまいます。そこで私は下記の手順でキャッチアップしています。

🗒️ NotebookLMを活用する

NotebookLMとは Google が提供するAI搭載のリサーチアシスタントです。

notebooklm.google

PDFやWebサイトの内容を要約し、チャット形式で質問に回答してくれる優れものです。他のAIツールと違い、引用を表示して生成結果の明確な根拠を示すため、回答を安心して確認できます。チャットと壁打ちしながら効率よくキャッチアップすることができます。

ここで重要なのは、自分たちが使いそうな一部のAPIだけでなく、仕様全体を把握しておくことです。そうしないと先方システムの構造や設計思想が見えてきません。単に「使えるかどうか」ではなく、「どういうデータ構造で動いているか」「どんな前提で設計されているか」まで理解するつもりでキャッチアップしましょう。

🧰 サンドボックス環境で動かしてみる

API仕様の概要をつかんだら実際に手を動かして動かしてみましょう。開発に着手する前に、サンドボックス環境や UAT 環境を早めに用意してもらうことが重要です。仕様書を参照しつつ Postman や curl でリクエストを送り、返ってくるレスポンスと突き合わせれば、初期段階での認識のズレを発見できます。成功系だけでなく異常系も意図的に試し、API の癖や制約を肌で感じておきましょう。仕様は読むだけでなく、自分の手で動かしてこそ真に理解できます

🤖 実装はAIにサポートしてもらう

API仕様を元にクライアントコードを実装するのは意外と時間がかかります。ライブラリやSDKがある場合はそれほど難しくはないのですが、提供されていない場合の方が多いのではないでしょうか。OpenAPI形式でAPI仕様が提供されていれば、ライブラリを使いコード生成できる場合もありますが、「スキーマとライブラリの相性が悪く、想定通りのコードが生成されない」ことも多々あります。そこで私は Cursor を使ってクライアントコードを自動生成させています。具体的な手順をご紹介します。

🐣 雛形となる実装用意する

外部 API を大量に扱う開発では、「同じパターンのコードを迷いなく量産できる仕組み」を先に整えておくことが肝心です。チームやプロジェクトのアーキテクチャにもよりますが、1 API = 1 ファイルにしておくと AI への指示が簡潔になり、変更による破壊も抑えられます。弊社ではGoを採用しており、下記のような構成にしています。

/gateway/external_hoge_service/
├── client.go          // API クライアントの共通処理
├── get_users.go       // 各エンドポイントごとの実装(1API1ファイル)
└── get_users_test.go  // httptest を使ったユニットテスト

client.go

type HogeAPIClient struct {
HTTPClient *http.Client
    BaseURL    string
}
func NewHogeClient(baseURL string, httpClient *http.Client) *HogeAPIClient {
return &HogeAPIClient{BaseURL: baseURL, HTTPClient: httpClient}
}

/users というAPIを叩いてユーザー情報を取得するAPIをコールするコードを書いてみましょう。

get_users.go

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

func (c *HogeAPIClient) GetUsers(ctx context.Context) ([]User, error) {
    url, err := url.JoinPath(c.BaseURL, "/users")
    if err != nil {
        return nil, err
    }

    resp, err := c.HTTPClient.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("Failed to get users, status: %s", resp.Status)
    }

    var users []User
    if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
        return nil, err
    }
    return users, nil
}

🧑‍💻 AIに実装依頼

ここまでできたら、下記のように指示するだけでOKです。

@get_users.go を参考に @https://api.document.com/xxx のクライアントコードを実装して!

これだけでAPI仕様を元に既存コードに合わせてクライアントコードを実装してくれるようになりました(モデルによって出力の精度に差があるかもしれません。私は claude-4-sonnet を使っていました)恥ずかしながら当時の自分は cursor rule の存在を知らなかったのですが、「コード生成用の cursor rule」を作成しておけばより精度の高いコードが生成できると思います。ただしAIが生成してくれたコードが実際に動くかはわからないので、ローカル環境で動作確認することをオススメします。

👮 データ整合性を守るために

🐌 再実行可能な状態にしておく

処理が失敗した場合に即座リトライできる仕組みを構築しておきましょう。私は「定期バッチ」や「Pub/SubのようなMQシステム」に処理を委ねることでリトライ可能にしています。(※Pub/Subではエラー時のリトライ機能をデフォルトで兼ね備えています)こうしておけば、一度目で失敗した場合に、時間をあけてリトライすることでデータの整合性を保つことができます。

ただしこのやり方には2つ注意点があります。1つ目は「一時的にデータ不整合が起きる期間をどこまで許容できるか」を考慮する必要があります。どうしても一度目と二度目の処理の間には一定時間が空いてしまいます。もしリアルタイム性を求められるシステムであれば、この手法は不向きかもしれません。

2つ目は「二度目の施行でデータ不整合が発生してしまうケース」です。例えば1つの処理内で外部システムのAPIを2回叩く必要があるとしましょう。

②のAPIコールに失敗し、リトライされてしまうと再度①のAPIがコールされることになり、外部システム側にデータが二重で登録されてしまいます。これを防ぐためにリエストごとに冪等キーを発行したり、事前にGETのAPIを実行して状態チェックを行う必要があるので注意しましょう。

🐯 トランザクション管理を工夫する

トランザクション管理とは、「複数の処理を一つのトランザクションとして扱い、その全体が成功するか、または全体を失敗としてロールバックする仕組み」のことです。単一DBでのトランザクション管理であれば簡単ですが、外部システムのDBは当然外部システム側で管理されてるため複雑になります。この問題を解決するために2つのアプローチをご紹介します。

1つ目は「処理の実行順を工夫する」ことです。例えば下記のパターンを見てみましょう。

上記だと③で失敗した時点で既に外部システム側にデータ登録されてしまっているため、ロールバックできなくなってしまいます。ですが下記のように②と③の順番を入れ替えてみるとどうでしょうか。

このように外部システムへのリクエストを処理の最後にしておけば、失敗した場合でも自社のDB内の操作を取り消すことができます。ですがこの方法も「ロールバック自体が失敗する可能性」もあるので、何かしらの形で失敗を検知する仕組みやリトライする仕組みは整えておく必要があるので注意しましょう。

2つ目に「外部システム側の取り消しAPI」を実行する方法です。俗にいう補償トランザクションと呼ばれるものです。シンプルな処理であれば「処理の実行順を工夫する」だけで対策できるのですが、下記のような「外部システムの処理」を待たないといけない場合だとどうでしょうか?

このような場合だと「外部システムへの登録」を行わないと後続の処理を実行できないので、処理の順番を変えるわけにはいきません。ですが、DB保存が失敗すると「外部システム側にデータが作成された状態」になりデータ不整合が発生してしまいます。

そこで「補償トランザクション」の出番です。下記のように「DB保存に失敗した場合に、外部システム側の取り消しAPI」を叩くことで、処理自体を無かったことにします。

こうすれば整合性は保たれますが、これも取り消しAPIの実行に失敗した場合は不整合が起こるので、やはりエラー検知やリトライの仕組みなどは整えておく必要があります。

🐜 あえて放置するのも状況によってはアリ

すべての不整合を完璧に解消しようとすると、システムは際限なく複雑になります。ユーザーには一切影響せず、コストも発生せず、UI にも現れないようなデータであれば、「放置してもよい条件」 を事前に定義して無視する判断も有効です。放置の基準をドキュメント化し、チームで合意しておけば、過剰な防御ロジックを実装せずに済み、開発・運用コストを大きく抑えられます。重要なのは、放置するか修正するかを 意識的かつ一貫性をもって選択 し続けることです。

🙌 さいごに

外部連携機能の実装は、仕様の読み解き、実装の工夫、そして地道な検証の積み重ねが求められる、なかなか骨の折れる仕事です。ただ一方で、他社のプロダクトとつながることで自社のサービスに新たな価値を生み出せる、非常にやりがいのある領域でもあります。

この記事で紹介した内容は、私自身が実務の中で試行錯誤しながら培ってきた知見を整理したものです。特に「開発の進め方に正解がなくて不安…」「何から手を付けるべきか悩む…」といったフェーズにいる方にとって、少しでも指針になれば嬉しいです。

また、最近ではCursorやClaudeなどのAIツールを活用することで、実装効率も大きく向上するようになってきました。手作業で消耗するのではなく、AIの力を借りながら、賢く・楽しく開発していきましょう!

最後まで読んでいただき、ありがとうございました!