🫲

バックエンド目線で見る「フロントにとって使いやすいAPI設計」

に公開1

はじめに

バックエンドエンジニアとして API を設計・実装していると、

  • フロントから「この API 使いづらいです」と言われる
  • 仕様通りに作ったはずなのに、フロント側で加工コードが増えている

といった経験はないでしょうか。

それはスキル不足ではなく、「バックエンド視点だけで API を設計している」 ことが原因であるケースがほとんどです。

この記事では、バックエンドエンジニアがフロントエンドの実装・状態管理を理解した上で 「使いやすい API とは何か」を説明できるようになる ことを目的に、実務視点で API 設計を整理します。

1. 問題点:よくある「バックエンド都合 API」

❌ 実装は楽だが、フロントが困る例

GET /api/users
[
  {
    "id": 1,
    "first_name": "Taro",
    "last_name": "Yamada",
    "created_at": "2025-01-01T10:00:00Z",
    "updated_at": "2025-01-10T10:00:00Z"
  }
]

バックエンド的には自然です。

  • users テーブルをそのまま返している
  • 正規化されたカラム構成

しかしフロントから見ると、次の問題があります。

  • 表示に不要な項目が多い
  • 表示用の fullName を毎回組み立てる必要がある
  • 一覧画面と詳細画面で欲しい情報量が違う

👉 「DB に近い API」になっている

2. フロント実装を見据えた設計の 3 つの視点

API は DB の公開窓口ではなく、「画面のためのデータ供給口」 です。フロントエンドの実装負荷を下げるために、以下の 3 つの視点を意識しましょう。

視点1:画面(UI)単位でデータを考える

フロントは「エンドポイント」ではなく「画面」を作っています。

ユーザー一覧画面
- 名前
- メールアドレス
- ステータス

この画面のための API であれば、レスポンスはこうあるのがよいでしょう。

{
  "users": [
    {
      "id": 1,
      "name": "山田 太郎",
      "email": "taro@example.com",
      "status": "active"
    }
  ]
}
  • 表示に必要な形で返す
  • フロントでの加工を最小化する

👉 API は画面のためのデータ供給口

視点2:フロントエンドに判断を委ねない

バックエンドで判定できるロジックは、判断結果をそのまま返しましょう。

{
  "status": 1
}
// フロント側
const statusLabel = status === 1 ? "有効" : "無効";

この判断が複数画面に散らばると、表示ゆれや修正漏れの原因になります。

✅ 判断結果を返す(ロジックをバックエンドに寄せる)

{
  "status": "active"
}
const STATUS_LABEL: Record<string, string> = {
  active: "有効",
  inactive: "無効",
};

👉 業務ルールはバックエンドが責任を持つ

視点3:TypeScript の型を意識したスキーマ設計

フロントでは、API レスポンスはそのまま型になります。

type UserListItem = {
  id: number;
  name: string;
  email: string;
  status: "active" | "inactive";
};

ここで意識すべきことは、

  • null を返さない設計(可能な限り初期値や空配列にする)
  • フィールドの意味が型名から伝わること

👉 API = 型の設計でもある

3. 実践:フロントエンドが喜ぶ具体的な設計ポイント

原則を踏まえ、実務ですぐに意識できる具体的なポイントを整理します。

3.1 日時データは ISO 8601 で統一する

❌ フォーマットが曖昧な日付

{
  "createdAt": "2025-01-12 10:23:45"
}
  • タイムゾーンが不明
  • 画面・環境ごとに解釈が変わる
  • フロント側で毎回変換処理が必要

✅ ISO 8601 形式で統一

{
  "createdAt": "2025-01-12T10:23:45Z"
}
type User = {
  id: number;
  createdAt: string; // ISO 8601
};

👉 バックエンドは「日時の正解」を提供し、表示はフロントに任せる

3.2 null や undefined の意味を型で明確にする

❌ 意味が曖昧な null

{
  "nickname": null
}
  • 未設定なのか
  • 将来入る予定なのか
  • 仕様なのかバグなのか

フロントは毎回悩むことになります。

✅ 意味を型で表現する

{
  "nickname": "aki"
}
type User = {
  id: number;
  nickname?: string; // 未設定の場合は key 自体を返さない、もしくは明示的な null
};

👉 null を返す前に「この値は存在しうるのか?」を設計で決める

3.3 HTTP ステータスコードとレスポンス Body の責務分担

❌ 200 OK でエラー内容を返す

200 OK
{
  "error": "Unauthorized"
}
  • 成功か失敗かが HTTP レベルで判断できない
  • フロントで独自判定が必要

✅ HTTP ステータスと body を分けて設計

401 Unauthorized
{
  "code": "UNAUTHORIZED",
  "message": "ログインが必要です"
}

👉 ステータスコードは「結果」、body は「理由」を伝える

3.4 UI 制御のためのエラー設計

❌ エラー内容が曖昧な API

{
  "message": "error"
}

✅ フロントで分岐できるエラー(Error Code の付与)

{
  "code": "USER_NOT_FOUND",
  "message": "ユーザーが存在しません"
}
if (error.code === "USER_NOT_FOUND") {
  showToast("ユーザーが見つかりません");
}

👉 エラーは UI 制御のための情報

4. まとめ:フロントエンドの実装まで想像できるバックエンドへ

バックエンドがここまで考えられると、単なる実装者ではなく 「設計者」 としての価値が上がります。

バックエンド目線で「フロントにとって使いやすい API」とは、

  • フロントが迷わないデータを返す
  • 型で仕様を説明できる
  • 実装ではなく設計で負担を減らす

API を「実装物」ではなく、フロントとのインターフェースとして設計できると、チーム開発はよりスムーズになります。

バックエンドだからこそ、フロントの実装まで想像した API 設計を意識してみてください。

Discussion

horie-thorie-t

システムが進化するとAPIの利用者は、フロントエンドだけではなくて別のバックエンド(別のマイクロサービス)になりますよ。

その時には、画面仕様に合わせたAPIは後悔しますよ。画面のリニューアルの時もバックエンドの大改修が入って、プロジェクトマネージャーから何でそんなに工数がかかるの?って詰められそう。

画面の為には、Backend For Frontendを挟むとか、REST APIではなくGraphQLにするとかがセオリーかな。