メルカリShops APIの紹介

はじめに

こんにちは。ソウゾウの Engineering Managerの@napoliです。「メルカリShops [フライング] アドベントカレンダー2022」の11日目を担当します。2022年11月にリリースされたメルカリShops Public APIについて、そのアーキテクチャや技術的な背景を紹介させて頂きたいと思います。

メルカリShops Public APIとは

メルカリShopsはメルカリのアプリ上でサービスを展開しており、あるお客様がメルカリ内に自分のショップを作りたいと思った時に簡単に自分のショップを作ることができます。普段のメルカリの操作と同じような感覚でショップがすぐ開設でき、スムーズにショップ運営をスタート出来る点がメルカリShopsの特徴であり、強みでもあります。

一方で、中規模、大規模のショップ様の場合、商品や注文の管理のために独自のシステムを構築されているケースがあります。そのようなショップ様の場合、メルカリShopsだけでなく、他のモールに対しても商品を出品していることがあります。こういったケースの場合、ショップ様が各モールの独自のUIに従い商品や注文の管理、お問い合わせ対応を手動で行うのはとても手間がかかるため、ショップ様が持つシステムと連動して、メルカリShopsの商品や注文を一元管理したいというニーズがあります。

メルカリShops Public API(以下、メルカリShops API)を利用すると、ショップ様が持つシステムと連携してメルカリShops内の商品や注文などの基本的な操作を行ます。

主な機能としては以下のとおりです。

  • 商品の登録、更新、削除、参照
  • 商品在庫の登録、更新、参照
  • 注文の完了処理、キャンセル、参照
  • 取引メッセージの追加、参照
  • Webhookによる注文イベントのリアルタイム受信

ショップ様はこれまで手作業で行っていた商品登録や在庫管理、注文管理などを、ショップ様のシステムと連動して効率良く行うことができるようになります。
(現在はあらかじめソウゾウとパートナー契約を結んだショップ様のみ利用可能です。)

GraphQLの採用

メルカリShops APIではGraphQLを採用しています。APIでは一般的にRESTが提供されることが多いと思いますが、メルカリShopsはこれまでBackend for Frontend(BFF)にGraphQLを採用しており、まずはその運用ノウハウや実績を活かす形でGraphQLをAPIとして採用しました。

GraphQLはRESTと比べ、利用者とAPI提供者双方に様々なメリットがあります。

  • 必要なフィールドのみを指定できるため、必要以上のデータを取得する必要がない
  • 呼び出しを1つにまとめることができるので、API呼び出し頻度を抑えられる
  • 型を持つスキーマによってAPIが定義されているので、開発者とのコミュニケーションが容易かつ安全

RESTと比べやや習得のコストは高くなりますが、十分にそれを上回るメリットがあると考えています。なお、開発者のニーズに広く対応するため、RESTにも今後対応予定となっています。

System Architecture

メルカリShops APIの簡易的な全体図は以下の通りです。比較のため、一部既存のメルカリShopsのシステムも記載されています。青枠で囲まれている部分が既存のシステムで、赤枠で囲まれている部分がメルカリShops APIを提供するにあたって新たに追加されたサービスになります。

図のように、Backendは既存のサービスを利用する形で構成されています。メルカリShopsは当初からマイクロサービスアーキテクチャで設計しており、それが活かされた形になっています。API提供のためのビジネスロジックを1から書くことはほぼ無く、比較的少ない工数で全体を構築できたと思っています。

ここから、APIを提供するにあたって新しく追加されたサービスと機能について、その概要と背景を紹介していきます。

Public API Gatewayの誕生

メルカリShops APIはメルカリ外部からのリクエストを受け付けます。Backendのサービス群は外部からのリクエストを直接受け付ける想定では設計されていないため、なんらかのサービスが外部からのリクエストを受け付ける必要があります。メルカリShopsのサービスに使われている既存のgraphql-serverが当初その候補に挙がりましたが、graphql-serverはあくまで内部的なサービスとして設計されており、外部にそのままAPIとして公開するにはセキュリティ上のリスクがありました。「graphql-serverの中で外部に公開するAPIのみ、内部のものと分けてはどうか」という案もありましたが、graphql-serverはNestJSで構築されており、NestJSのフレームワークの上で複数サーバーを提供することには難しさがあったため、シンプルにリクエストを受け付ける別のサービスを立ち上げようということになりました。それがpublicapi-gateway(以下、Public API Gateway)になります。

内部トークンとPersonal API Access Token

どのようなサービスであれ、サーバーは「このリクエストは誰からのものか」を正しく認識する必要があります。これを一般的には「認証」と呼びます。

既存のメルカリShopsのサービスでは認証のために「内部トークン」を発行しています。内部トークンの実態はIDトークンです。IDトークンとは認証されたユーザーに関するクレームが含まれる、JSON ウェブトークン (JWT) です。内部トークンはお客様のブラウザ(アプリの場合WebView)に保存され、サーバーへの各リクエストに付与されます。メルカリShops内部の各Backendサービスはこの内部トークンによってアカウント(お客様)を特定しています。

しかしこの内部トークンは人間がブラウザやアプリを通してメルカリShopsを操作する目的で発行されており、これをそのままAPIの認証トークンとして利用することは利便性の面で適当ではなかったため、新しくPersonal API Access Token(以下APIトークン)というAPI専用の新しいトークンを発行することにしました。

APIトークンの仕組みはシンプルで、1つのAPIトークンが1つのショップに紐付きます。内部トークンはお客様のアカウントに紐付いていますが、APIトークンはショップに紐付く点が異なります。正確には内部的にはAPIトークンはアカウントに紐付いてはいるのですが、内部で自動的にショップと結びつけているため、クライアントから見るとショップ単位で発行されているようにみえます。厳密にアカウントと紐付けると、アカウントはショップを1つしか持っていないにも関わらず、shop-idなどの識別子を毎回リクエストに求めることになってしまい不便です。またそもそもshop-idを何らかの手段で予めパートナーに伝える必要があります。このあたりは実態に沿って、APIトークンが1つのショップに紐付いているように見えるようにして、クライアントがAPIを使いやすくなるよう工夫をしました。

各ショップ様はメルカリShopsのショップ管理画面からこのトークンを発行することができます。またトークンは複数発行することができます。


(メルカリShops PC版 ショップ管理画面)

現状では機能をシンプルにするために、このトークンを使うことでそのショップに対してAPIのすべての操作を行うことができます。またトークンの有効期限も設けていません。その分、各ショップは発行したトークンを慎重に取り扱う必要があります。不要になった、もしくは万が一トークンが漏洩したときは直ちに無効化させる必要があるので、ショップ管理画面にトークン無効化(削除)の機能も追加しています。

トークン発行機能の必要性

ショップ管理画面によるトークン発行機能を実装する以前は、「リリース当初は接続パートナー数も多くないから、メルカリShops側でAPIトークンを発行して手渡しで渡せばよいのでは?」と考えていました。しかしそのパートナーがいちショップ様で、独自のシステムを持っているケースなら良いのですが、パートナーの中には複数のモールへの出品を一元的に管理できる、いわゆるASPサービスを提供する事業者も存在します。ASPサービスはショップ様とメルカリShops APIの間に立ち、各モール間で統一的なインターフェイスを顧客(ショップ様)に提供しています。

このASP事業者のケースでは、APIトークンが必要なのはASP事業者ではありません。彼らはメルカリShopsに直接出店しているわけではないからです。APIトークンが必要なのはあくまでASP事業者様の先にいる顧客です。ASPが抱えている顧客は事業者のよってはかなり多くなるため、それらの顧客がメルカリShopsに接続するたびに手動でAPIトークンを渡すのは現実的ではありません。そのためショップ様(ASP事業者の顧客)がいつでもトークン発行が行える機能が必要でした。

内部トークンへの変換

上記の「内部トークンとPersonal API Access Token」で説明したとおり、メルカリShopsでは内部トークンが各Backendサービスでの認証に利用されているため、既存のBackendサービスを使って機能を実現するには受け取ったAPIトークンを内部トークンに変換させる必要があります。この変換をPublic API Gatewayが「auth」という内部のサービスを利用して行います。

Public API GatewayはリクエストヘッダからAPIトークンを抽出し、authサービスに渡します。authサービスはメルカリShopsの認証に関連する中心的な機能を持つサービスで、APIトークンもauthサービスから発行されています。APIトークンを内部トークンに変換する機能も持っているため、これを利用し、Public API Gatewayは内部トークンを取得します。取得に成功すると、内部トークンを付与した上でクライアントから受け取ったGraphQLのリクエストをそのまま(後述する)Public API GraphQL Serverに中継します。

Public API GraphQL

既存のgraphql-serverと同じく、NestJS上に構築されたGraphQLサーバーです。Public API Gatewayによって中継されたGraphQLのリクエストを受け取ります。GraphQLのオペレーションを解釈し、適切な処理を行い、レスポンスを返します。具体的な処理はBackendのサービスにそのまま委譲することが多いですが、ビジネスロジックも一部実装されています。各Backendのサービスはもともとメルカリ Shops APIのために設計されたものではないので、このPublic API GraphQL ServerがAPI向けに機能を調整する役割も担っています。

Public API GraphQLはgraphql-serverで培ったノウハウが大きく活かされています。ただし、graphql-serverはメルカリShops開発当初やや手探りで構築されたところもあるので、改善したほうが良い点もいくつかありました。Public API GraphQLではその改善を取り入れ、これまでの実績の上で調整も加えたバランスの良い構成になったと思っています。

Introspection Query

メルカリShops APIではIntrospection Queryをサポートしています。

Introspection QueryとはGraphQL APIがどのようなQueryやMutationをサポートしているのか、どのような型情報を持っているかといった情報が定義されたIntrospectionを取得するためのQueryです。

これにより開発者(ショップ様)は容易にAPIの全体像を把握することができます。またこのIntrospection Queryを利用することで開発者はメルカリShops API向けのGraphQL Clientを各言語(typescript, python, golangなど)にあわせて自動生成することも可能となり、開発効率の向上が期待できます。

Altair GraphQL ClientPOSTMANなどのツールを使ってIntrospectionを取得するとGraphQL APIがどんなQueryやMutationを提供しているのかを分かりやすく確認できたり、デバッグも簡単に行えて便利です。


Altair GraphQL Client

当初はセキュリティ上、Introspection Queryは提供せずあくまでAPI Documentにて仕様を共有すべきではという議論もありましたが、Introspection Queryを使えるのとそうでないのとでは開発者の利便性が大きく変わるため、提供しようという結論になりました。ただしセキュリティ面を考慮し、Introspection Queryの取得にもAPIトークンを要求することにしました。

Rate limiting

Rate limitingとはある一定のルールを元に、特定のクライアント(ショップ様)に対しAPIへのアクセスを制限する仕組みのことです。クライアント側の実装に問題があったり、バグなどにより意図せず過剰なリクエストが行われてしまうことがあります。それによってAPI全体のパフォーマンスの悪化に繋がることがあるため、クライアントごとにリクエストを適切に制限する必要があります。

GraphQLはクライアントのQueryの書き方によって比較的簡単に「重い」リクエストを作れるため、パフォーマンスを健全に保つためリリース当初よりRate limittingは必要と考えました。

このRate Limitingによる制限を実現するため、メルカリShops APIでは「ratelimit」というサービスを新たに作りました。ratelimitサービスはenvoyproxy/ratelimitを利用して構築されています。

Public API GraphQLは受け取ったGraphQL QueryのComplexity(複雑さ)を計算し、ratelimitサービスにその情報を渡します。ratelimitはその情報をもとにリクエストを行ったクライアント(ショップ様)に制限を掛けるべきか判断し、その情報を返します。制限を掛けるべきであればPublic API GraphQLはエラーを返却します。

またPublic API Gatewayもratelimitサービスにアクセスします。Public API GraphQLとratelimitサービスとの連携により既に制限状態であると判断されれば、後段のサービスにリクエストは中継せず、エラーを返却します。

Sandbox

メルカリShopsでは開発者(ショップ様)がAPIのテストを行うために、開発者用のテスト環境が用意されています。

一般的に、APIのテスト環境としては大きく2種類が考えられます。

  1. 本番環境にテスト用の機能を追加し、本番環境のインフラ上でテストが行える環境
  2. 本番環境とは独立したインフラでテストが行える環境

メルカリShopsでは後者の「本番環境とは独立したインフラでテストが行える環境」方式を採用しており、その環境を「Sandbox」と呼んでいます。前者の方式も検討しましたが、テスト機能開発コストと安全性のバランスを考え、後者を採用しました。

Sandbox専用のDebug API

メルカリShops APIのSandboxはメルカリShopの内部のテスト環境と同じ環境をシェアしているのですが、セキュリティの都合上、この「内部のテスト環境」は(パートナー含め)非公開となっています。そのためAPIを開発するショップ様はいわゆる「メルカリShopsテスト環境」を触ることができません。メルカリShopsテスト環境を触ることができないということは、たとえば「Sandboxに登録した商品を購入して注文情報を作る」ということができず、スムーズにテストを行うことができません。

この記事の執筆時点ではメルカリShops側でショップス様向けに手動でテスト用の注文を作る必要があり、開発効率の悪さにつながってしまっています。そこで今後開発効率を向上させるために、Sandbox向けにいくつかの専用のDebug APIを提供する予定になっています。具体的には

  • 任意の商品を購入するAPI
  • 任意の注文に対して購入者からの取引メッセージを送信するAPI

などが利用できるようになります。
これらAPIを利用することで開発者は特定の機能において効率よくデバッグすることができるようになる想定です。

おわりに

メルカリShops APIのアーキテクチャやその技術背景について簡単に紹介させていただきました。APIはシンプルで使いやすいものとなっていますが、機能としてはまだまだミニマムな構成になっています。これからパートナーからのフィードバックを頂きながら、より使いやすいAPIを目指して成長させていきたいと思っています。

Souzohでは現在以下のポジションでメンバーを募集中です。SouzohのMove Fastな組織に共感した方はぜひ以下のページをご覧ください!

またカジュアルに話だけ聞いてみたい、といった方も大歓迎です。こちらの申し込みフォームよりぜひご連絡ください!

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加