【Open API 考察】FE がAPI mock を作成するときに気をつけること


フロントエンドエンジニアの茶木です。甘党です。

API でデータを Getして表示したり、変更データを Putしてデータベースを更新したい要件があります。

フロントエンドは表示部分の実装を進め、APIの部分は Open API で mock を作ります。そして後ほど、バックエンドに mock どおりで API の実装が問題ないかレビューしてもらうという形をとっています。

先に結論

フロントエンドエンジニアが API mock を作成するときに気をつけると良いのは

  • デザイン上の構造とデータ構造は必ずしも一致しないので留意する
  • API mock はネストせずフラットに書くべきかを考慮する

API からGetしたデータを表示し、編集結果を Putする管理画面

それはなぜかという話をします。

例として、パンケーキを提供するお店のメニューを編集する管理画面を作るとしましょう。
メニューの名前と、トッピングとアイスに何がつけられるパンケーキなのかを設定できます。

たとえばこんな感じのメニュー画面です。

素直に考えた TypeScript 型

interface Props {
  id: string;
  name: string;
  topping: {
    butter: boolean;
    syrup: boolean;
    cream: boolean;
    jam: boolean;
  };
  iceCream: {
    vanilla: boolean;
    strawberry: boolean;
  };
}

デザインを見て素直に考えると、idと商品名( name )それにトッピング( topping )とセットアイス( iceCream )でグルーピングするのが良さそうです。トッピングの保存ボタンとセットアイスの保存ボタンを押したときに、PutAPI を呼び出すことを考えても、トッピング、セットアイス単位のグルーピングは自然に見えます。

素直に考えた Open API

components:
  schemas:
    Topping:
      type: object
      title: Topping
      additionalProperties: false
      properties:
        butter: # バター
          type: boolean;
        syrup: # シロップ
          type: boolean;
        cream: # クリーム
          type: boolean;
        jam: # ジャム;
      required:
        - butter
        - syrup
        - cream
        - jam
    IceCream:
      type: object
      title: IceCream
      additionalProperties: false
      properties:
        vanilla: # バニラ
          type: boolean;
        strawberry: # イチゴ
          type: boolean;
      required:
        - vanilla
        - strawberry

つづいて mock API 用に、同じようなデータ構造を考えると、ToppingIceCream という Object 型がそれぞれあると良さそうですね。

    GetResponse:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        topping:
          $ref: '#/components/schemas/Topping'
        ice_cream:
          $ref: '#/components/schemas/IceCream'
      required:
        - id
        - name
        - topping
        - ice_cream
    PutRequest:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        topping:
          $ref: '#/components/schemas/Topping'
        ice_cream:
          $ref: '#/components/schemas/IceCream'

ToppingIceCreamidname を加えて GetAPIの取得データの型( GetResponse )と PutAPIの更新データの型( PutRequest )ができました。GetAPI はページ表示に使うため全データを取得します。よって、各 props は required です。PutAPI は更新したい props だけを指定して送信する想定のため、 required ではありません。

APIの構造化を進めすぎると仕様変更に弱い

これで、全く問題ないように見えます。

しかし、ここで仕様変更があったとしましょう。

トッピングというカテゴリーを、基本とスペシャルに分ける、セットアイスというカテゴリーは廃止して、スペシャルトッピングに組み入れたい。

といった内容です。これはありそう。

この変更依頼では、PutAPI で更新できる単位が、トッピングとセットアイスで固定していたために、変更後の基本トッピングやスペシャルトッピング単位での PutAPI がコールできません。GetAPI も、もはや意味のない構造のネストを返します。

こうなると、API の更新が必要になります。

設計を考え直す

    GetResponse:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        butter: # バター
          type: boolean;
        syrup: # シロップ
          type: boolean;
        cream: # クリーム
          type: boolean;
        jam: # ジャム;
          type: boolean;
        vanilla_ice_cream: # バニラアイス
          type: boolean;
        strawberry_ice_cream: # ストロベリーアイス
          type: boolean;
      required:
        - id
        - name
        - butter
        - syrup
        - cream
        - jam
        - vanilla_ice_cream
        - strawberry_ice_cream
    PutRequest:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        butter: # バター
          type: boolean;
        syrup: # シロップ
          type: boolean;
        cream: # クリーム
          type: boolean;
        jam: # ジャム;
          type: boolean;
        vanilla_ice_cream: # バニラアイス
          type: boolean;
        strawberry_ice_cream: # ストロベリーアイス
          type: boolean;

ネストせずフラットに書き直しました。これにより個別の props の更新が可能になりました。複数の props を更新したい場合でも必要なものを全てセットすれば良いのです。これであれば、取得/更新するデータが変わらない限りは API の変更は必要ありません。

なお、必要であれば、基本トッピングやスペシャルトッピングといった構造体への組み立てはフロントエンドで取得後に行います。

フロントエンドエンジニアが考えるべきこと

  • 境界面での変更はフロントエンドで吸収する
    (APIの変更は、フロントエンドの変更よりやりにくいことを知る)
  • API mock を作る段階で、更新性が高い設計になっているか考える
  • ネストを使う強い理由がなければフラット化を考える

別解

少し結論がブレますが、フラット化より前に別解がこのケースではありそうです。バターもクリームもアイスも一律にトッピングとみなして、トッピング( Topping )の配列 を用意することが最適かもしれません。バナナやチョコレートソースなど、新トッピングが登場することはあるでしょうから、任意のトッピングを追加・編集できる構造が望ましいです。

components:
  schemas:
    Topping:
      title: Topping
      type: object
      properties:
        key: # butter など
          type: string;
        label: # バター など
          type: string;
        value:
          type: boolean;
      required:
        - key
        - label
        - value
    GetResponse:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        toppings:
          type: array
          items:
            $ref: '#/components/schemas/Topping'
      required:
        - id
        - name
        - toppings
    PutRequest:
      title: GetResponse
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        toppings:
          type: array
          items:
            $ref: '#/components/schemas/Topping'

開発上考えるべきこと

フロントエンドエンジニアの癖

フロントエンドエンジニアは(自分は)デザインからDOMを想像する過程で、ツリー構造で理解を試みる傾向が強いので、そのままロジックに移していいか一考が必要だと思いました。

  • デザイン上の構造とデータ構造が、必ずしも一致しないことに留意すること
  • デザイン上の構造とデータ構造の差異を吸収するのはフロントエンドの使命

これらを、忘れないようにしようと思いました。

見た目から構造を考えてはいけない

そもそも、見た目から構造を考えるのは良くなくて、要件や、さらに手前の実現したいシナリオを理解が先にできるのが望ましく、その上で、最適な設計があるのだと考えました。

開発のお悩み、フロントエンドから解決しませんか?

あなたのチームのお悩みはなんですか?

「バックエンドエンジニアにフロントエンドまで任せてしまっている」
「デザイナーに主業務以外も任せてしまっている」
「すべての手が足りず細かいことまで手が回らない」

役割や領域を適切に捉えてカバーし、チーム全体の生産性と品質をアップするお手伝いをします。
フロントエンド開発に関わるお困りごとがあれば、まずは一度お気軽に Gaji-Labo にお声がけください。

オンラインでのヒアリングとフルリモートでのプロセス支援に対応しています。

リモートワーク対応のパートナーをお探しの場合もぜひ弊社にお問い合わせください!

お悩み相談はこちらから!

タグ


投稿者 Chaki Hironori

webライターもやってるフロントエンドエンジニアです。Reactは自信があります。またデザイン畑の出身で、気持ちのいいアニメーションやインタラクティブな表現は丁寧に手掛けます。好きなものは中南米の遺跡で、スペイン語が少しできます。