はじめに
NearMeでは最近、相乗り配車サービスのための外部向けAPIプラットフォームを構築しました。 これにより、他アプリからシームレスに注文したり、Lineミニアプリのような新しいチャネルのUIを独自に構築することを可能にしました。 その設計においては様々な考慮が必要でしたので、ここにまとめたいと思います。
提供方法
APIを利用するにはまず、外部連携先の"組織"を作成し、登録した"組織"で「〇〇 地域シャトル」「〇〇スクール送迎」などの"サービス"を作成します。これにより、ユーザー管理、車両管理、注文管理などが管理画面から利用できるようになります。マルチテナント方式なので専用の"サービス"が構築されます。
次に、API連携に関する基本情報を格納する"アプリケーション"という項目を作成します。 認証情報やWebhookのURLなどもここで設定します。 この"アプリケーション"のIDがキーになって、例えば、この注文はこの外部連携から作成されたもの、というのが分かるようになります。
最後にAPIを試用します。 Jupyter Notebook で主要なAPIをすぐに叩けるようにしつつ、 APIを網羅的に整理するために、 PostmanというAPI確認用ツールも利用しています。 Postmanのいいところとして、 変数を保存しておいて環境ごとに切り替えられる点、 API実行の前と後にスクリプトを走らせて変数を操作できる点、 API毎にドキュメントを書けたり、レスポンスを保存して表示できる点、 一覧性が高く、見やすい点、 などが挙げられます。 次図は実際のPostmanの使用例です。
認証
リソースにアクセスするための認証は、"組織"に関するものと、各ユーザーに関するものに分かれます。
"組織"に関するものは server_token
を利用します。
信頼のあるサーバーから、このトークンをリクエストヘッダに付与してリクエストします。
各ユーザーに関するものは、access_token
をリクエストヘッダに付与してリクエストします。外部ユーザー連携を行う場合は、 server_token
を利用して "アプリケーション"に紐づいた形でNearMeのユーザーを作成した上で、server_token
を利用して、そのユーザーのaccess_token
を取得します。そうでない場合は、OAuth形式に沿って、client_id
と client_secret
を利用します。権限タイプに応じた"ユーザーの認証情報"とともに、信頼のあるサーバーを経由して付与されたclient_id
と client_secret
をログイン用のAPIにリクエスして access_token
取得します(参考、※ パスワードタイプはテスト用途でのみ利用しています)。
Webhook
通知の連携や、システム間の状態の連携を行うときにWebhookを利用します。 例えば、"運行が完了した"というようなイベントを、 NearMeのシステムから外部のシステムに送信します。 プッシュ通知などは外部のユーザーに対してNearMeのシステムから直接送ることはできないので、 このWebhookにより、外部のシステムを介して通知を送ったりします。
WebhookのURLや認証情報なども"アプリケーション"に保存します。
Webhookの認証は外部システム依存になるので、ここは柔軟に設定できるようにしています。
ただし、デフォルトで、
client_secret
をキーにして、SHA256でリクエストボディをハッシュ化した値をシグネチャとして用いて、
送信元の検証が行えるようにもしています(参考)。
中継サービス
元々、内部向けAPIは既に用意はされているので、 それを権限だけ修正してそのまま公開というのもできなくはないですが、 外部向けAPIは安定性や認知性をより重視するので、 リクエストを中継するサービスを新たに設けて、その特性の違いを吸収させました。
GraphQLかRestAPIか
特に、内部向けAPIはGraphQLで組んでいたのですが、外部向けAPIはRestAPIで組むようにしました。
GraphQLのいいところは、 型に厳密なので間違いに気づきやすく、 柔軟性が高いので効率良くデータを取得できるといった点があります。 逆に、少しのスキーマ変化でエラーを起こしたり、 どんなクエリを作成すればいいか分かりづらかったりします。 GraphQLそのものへのキャッチアップも少し時間がかかります。
一方、RestAPIはその点、少しのスキーマ変化でもレスポンス自体はエラーにならず、 どのAPIをどのように叩けばいいのかはより明快です。 逆に、スキーマ変化のためにエラーが混入しやすく、 不要なデータを取ってきたり、何回もAPIを叩いたりする必要があったりします。
内部で開発する分にはGraphQLの不利点はそこまで問題にならないのですが、 外部の人がAPIを利用する際はRestAPIの不利点よりもGraphQLの不利点の方が大きくなるのではと思います。
名前の変換
外部向けAPIはRestAPIで組むとしたことで、 中継サーバーにおいてGraphQLからRestAPIへの変換が必要になってくるのですが、 名前をリファクタリングできる機会も生まれたので、結果よかったと思います。
外部向けAPIは一度公開したらなかなか変えられないので、 なるべく熟考して名前を決めたいところです。 内部向けAPIでもそれは要求されますが、 やり直しのチャンスはもう少し残っています。 割り切る時もあるかもしれませんし、開発が進んでからより良い名前を思いつくこともあります。
例えば、配車のステータスといったフィールドの値は、 内部向けAPIでは数値で管理していたのですが、 分かりやすい文字列に変換するようにしました。 数値だとデータ節約の側面もありますが、名前を深く考えなくていい(コード上で名前は設定しますがこれは後から容易に変更できます)、というのも開発の上で進めやすかったのですが、 外部向けには余計な不便をかけてしまいます。
おわりに
NearMeの外部向けAPIプラットフォームにおける、基本的な構築方法から、設計思想に関わる中継サービスについて説明しました。 構築にあたっては他サービスの事例を色々と参考にしてきました。この記事もまたその参考の一つになれば幸いです。
最後になりますが、NearMeではエンジニアを募集しています!ご興味のある方はぜひ以下をご覧ください。
Author: Kenji Hosoda