こんにちは、フロントエンドエンジニアの峯です。
突然ですがみなさん、フロントエンド開発を行う際、どのくらいバックエンド側の事を意識してますか?
バックエンド側の経験が無いとなかなか難しいところだと思います。
きっとフロントエンド、バックエンド双方で「こうだったら良いのにな…」なんて呟きながら仕事してたりするんじゃないでしょうか?笑
そこで、フレームワーク等を使った開発を行えば、担当領域の線引きがしっかりとできて、双方楽だったりします。
しかし、その場合フロント側でAPI設計を行う必要が出てきたり、どんなデータ欲しい?なんて問い合わせを受けたりするかと思います。
今回は、そんなAPI設計を行う場面で、最低限どんなところを意識して設計を行っていけばいいのかを紹介していきます。
目次
0. APIについて理解する必要性
React、Vue.js、Angularなどのフレームワークの登場によって、フロントエンドに要求される観点は広がりつつあります。
比例して、フロントエンドで完結できる領域が広がっていると言えます。
HTML、CSS、JavaScriptを使ってマークアップまでのフロント開発を行ってきた数年前までは、APIやデータについて意識する機会は少なかったかもしれません。
しかし、フレームワークの登場によって、フロント側でデータ管理を行う場面が出てきました。
「データをどのように管理するか。」
という観点で開発を行う必要がある以上、バックエンドエンジニアに欲しいデータを要求できると、双方にとってメリットは多いでしょう。
フロントエンド、バックエンドでモジュールを完全に切り分けることができれば、双方は自身のモジュールに専念して開発を行うことができるため、運用性は向上します。
そのために、完全に切り分けられたモジュールを繋ぐ、APIに関する理解を相互で揃える必要があるため、APIについて理解を深めていく必要があります。
1. 管理方法
APIはフロントエンド、バックエンドで共通認識を持つ必要があります。
ここで、齟齬が生まれるとインターフェースとして破綻してしまうので、しっかり仕様をどこかに明記して運用しましょう。
管理方法例
仕様変更や、機能追加時にはアップデートの必要があるので、運用性を重視しましょう。
- スプレッドシート
- ymlファイル(エディタのビュアープラグインなどがあります)
明記する情報
どのエンドポイントでどのようなデータが取得可能なのか、リクエスト、レスポンスの仕様等がわかるようにします。
- エンドポイント(
/users/:id/
など) - メソッド(
GET
、POST
、PUT
、DELETE
など) - ステータスコード(
200
、404
、501
など) - リクエストパラメータ
- レスポンスパラメータ
2. 構成・形式
APIの形式をどうするか、どのような構成(エンドポイント、含めるデータ)にするのか、ここは重要です。
ここが開発途中に変更になると、工数が一気に膨らみます。
しっかり検討して、バックエンドとコミュニケーションを取りながら、すり合わせを行って定義していきましょう。
(DB設計の都合もあるので、より双方のベストな仕様を選択します)
データ管理などは、バックエンドの方がベテランなことが多いと思うので、迷うことがあれば適宜相談するようにしましょう。
形式・型
APIの形式をどのようにするか?
大体JSONが多いのかな。(使いやすいし)
- JSON
- XML
ここで重要なのは、型やエンドポイント、リクエストパラメータなどをしっかり定義しておく事です。
nullを許容するのかしないのか、int型なのか、string型なのか。
フロント側でも型の違いがエラーの原因になります。
(エラーにならないようなロジックを作るのも重要です ※エラーハンドリング)
エンドポイント
APIを作成する場合、どのようなデータをどこで受け取るのか?
- 画面単位
- コンポーネント単位
一番シンプルな設計は画面単位でエンドポイントを分けるという方法です。
フロント側で表示する画面に必要なデータを画面単位で受け取る。
しかし、管理画面や中規模以上のWebサービスの場合は、画面単位では効率が悪い場合があります。
サービスの規模が大きくなると、データが複数の紐付きを持ちます。
紐付きを意識した設計を心がけましょう。
重複したリクエストをなくす事で、速度改善やサーバー負荷の軽減につながります。
例えば、ユーザー情報とユーザーに紐づく登録データのAPIを考える場合
- ユーザー情報:
/users/:id/
- 登録データ:
/data/:id/
※ id・・・ユーザーID
どの画面にもユーザー情報のユーザー名を表示させる場合、毎度ユーザー情報を取得するのは効率が悪いと言えます。
例えば、 ログインしたタイミングでユーザー情報をコンポーネントが保持して、そこから常に見せておくことができれば、ユーザー情報のリクエストは一度で済みます。
コンポーネントの作りによって、上記が難しい場合は、登録データAPIにユーザー名を含めてもらうこともできるかもしれません。
ここでは、登録データAPIはユーザーIDによって区別されているので、このような要求も可能だということです。
3. APIエラー
仕様が曖昧な場合に発生しがちなエラーを書き出してみました。
以下の発生を防ぐためにも仕様はなるべく固めておきましょう。
フロントエンド
- オブジェクトがnullでundefinedになる(オブジェクトの存在チェックで防げます)
- int型をstring型で受け取った型エラー(型チェック)
バックエンド
- PUTリクエストなどで、バック側で許可されていない値のリクエスト
- 必須なクエリパラメータの漏れ
この辺は、TypeScript活用することで開発しやすくなっていると思うので、勉強してみると良いかもしれません。
4. 認証
サービスにログイン機能がある場合は、認証をどこで行うかを考えます。
おそらくバック側では、必ず権限によってエンドポイント要求を制御するかと思います、フロント側でどこまで制御するのか確認しましょう。
- Cookies(例:認証後にバックエンド側から受け取ったCookie情報で認証を行う)
- Local Storage(例:フロントエンドのみの制御に利用する)
5. バリデーション
バリデーションは、どこまで見るのか区別する必要があります。
バック側でもバリデーション処理は行われるかと思いますが、ユーザーに常にステータスを示すことで、ユーザー体験の向上にもつながります。
ケース1(1つの入力情報)
入力されたデータに対するバリデーション
- 型(例:int型のみ許可)
- 文字数(例:10文字以内)
- 正規表現(例:URL、パスワード)
ケース2(複数の入力情報)
ユーザーの入力情報に対するバリデーション
例)
項目Aの選択内容に応じて、項目Zを制御するなど。
入力の矛盾を防止します。
ケース3(外部データ)
すでにDBに保存されているデータに基づいたバリデーション
ケース3は注意が必要です。
ケース3は、既存の登録情報等(DB)を見てバリデーションを行う必要がある場合です。
注意すべきことは、バリデーションの対象となる、DB情報がいつ、どのようなタイミングで変更されるのかがわからないということです。
変更されることがないのであれば、一度データを受け取ってしまえば構いませんが、変更が想定される場合、登録時(入力中)と、登録実行時(submit)で、DB情報に変更が加えられた場合に、バリデーションの結果が変化する可能性があります。
このような、バリデーション結果が曖昧なものは極力フロントエンドで行うのは避けたほうがいいでしょう。
まとめ
プロジェクトなどに応じて領域は変化します。
大事なことは、相互に領域を意識してベストなバリューを発揮できるかということだと思います。
領域が曖昧では、それが不安材料になるため、進捗を遅らせる原因になりかねません。
今回紹介した内容や例はあくまで一部に過ぎません。
API設計何すればいい?といった悩みや、経験が浅くて大事なポイントが定まっていないといった方にとってヒントになればと思います。
最後まで読んでいただきありがとうございます!