Zenn
🍇

【API】"RESTの原則"をアンチパターンを基に噛み砕いてみました

2025/03/16に公開

APIの設計とか開発とか、結構やること多いけど、ちゃんと抑えてねぇなぁと思い今回まとめてみました。

各原則の理解がふわっとでも理解できればと思い、アンチパターンを基に説明します。

結構長いですので、忙しい方はここだけでも 👋

では、さっそく6つの原則を紹介していきます。

と、その前にRESTってなに?

何度調べてもなんだかよく分からない概念ランキングの個人的 Top5 に入る言葉
それが「REST」だ

すべての Web サービス設計者に捧ぐ「RESTful って結局なんなんだ」

よくわかんないで有名なんですね。

Representational State Transfer (REST) は、大まかに言うと API の動作に条件を課すソフトウェアアーキテクチャです。

RESTful API とは?(aws) - RESTとは?

定義が抽象的すぎて、ピンとこないですよね。

私も調べてみました。
ただ、どのサイトも似たような表現がいまいち理解しにくいな、ってところが正直な感想です。
(人によって解釈が違いそう。なのでいろんなRESTがありそうですね。)

いろいろ調べた中で私はこう理解しています。

RESTfulの原則とは、RESTを満たすための原則(ルール)のようです。

調べてる中で感じたんですが解釈には個人差があるみたいで、RESTとは、"APIの設計思想"、"システムの設計思想"、"API設計手法"など、かなり理解の仕方が曖昧なようです。

私は、以下の記事参考にさせていただきました。
すべての Web サービス設計者に捧ぐ「RESTful って結局なんなんだ」

本記事では、「RESTについて」の深堀りはこの辺で...。

REST原則 について - 概要

REST原則自体も定義が曖昧みたいです。(4つだったり6つだったり意見が分かれることもあるみたいです)

今回は、以下6つご紹介します。

主語を意識すると理解しやすいかもです。

  1. Client-Server: クライアントとサーバーは役割を明確に分ける
  2. Stateless: APIは状態を保持せず、リクエスト単位で完結させる
  3. Cacheable: APIはキャッシュ可能にする(Statelessのため処理が重くなるから仕方ないですよね)
  4. Uniform Interface: (HTTPの場合)リソースへのやり取り(CRUD)はメソッド(GET, POST, PUT, DELETE)を適切に利用する
  5. Layered System: システムの設計はレイヤー構造に分け、拡張性を高める
  6. Code on Demand(Optional): クライアントにコードを送信・実行させる

さてここからは、アンチパターンを例に各原則を噛み砕こうと思います。

REST原則について - 詳細

原則①. Client-Server

クライアントとサーバーの責務をそれぞれ明確にしましょう。

  • Client: APIを利用しリソースのリクエストを行う
  • Server: リクエストに応じてリソースを提供する

🆖 アンチパターン(サーバーがHTMLを返す / クライアントがDBに直接アクセス)

サーバーがHTMLを直接生成して返す(モノリシックな設計).php
echo "<html><body><h1>" . $user_name . "</h1></body></html>";
クライアントが直接DBにアクセス.js
const result = database.query("SELECT * FROM users WHERE id = 1");
console.log(result);

🆗 デザインパターン(APIを通じてJSONを返す)

APIにリクエスト.js
fetch('/api/users/1')
  .then(response => response.json())
  .then(data => console.log(data));
JSON形式でレスポンスを返す.php
header('Content-Type: application/json');
echo json_encode(['id' => 1, 'name' => 'John Doe']);

原則②. Stateless

サーバーは状態を保持せず、すべてのリクエストは独立して処理されるべきです。
ここでいう "状態" はログイン状態、カートの状態、などだと理解しやすいかと。

🆖 アンチパターン(セッションを利用)

セッションを使ってカートの状態を管理.php
session_start();
$_SESSION['cart'][] = ['item' => 'Laptop', 'price' => 1000];
echo json_encode(['message' => 'Item added to cart']);

(ロードバランシング等で)サーバーが複数台ある場合、リクエストごとにカート状態がリセットされそうですね。突然カート状態が復活することとかも起きそう。ステートフルですね。

🆗 デザインパターン(クライアント側で状態を管理)
ローカルストレージにカートを保存.js
let cart = JSON.parse(localStorage.getItem('cart')) || [];
cart.push({ item: 'Laptop', price: 1000 });
localStorage.setItem('cart', JSON.stringify(cart));
カートの内容をAPIに送信(購入時).js
fetch('/api/checkout', {
  method: 'POST',
  body: JSON.stringify({ cart }),
  headers: { 'Content-Type': 'application/json' }
});
受け取ったカート情報を処理.php
$cart = json_decode(file_get_contents('php://input'), true)['cart'];
echo json_encode(['message' => 'Checkout successful', 'total' => array_sum(array_column($cart, 'price'))]);

これだと、ステートレスなのでどのサーバーにリクエストが送られても問題ないですね。
※ ローカルストレージだとセキュリティ面のケアが気になるところですが、本題ではないのでスルーしてください。

原則③. Cacheable

APIのレスポンスはキャッシュ可能であるべきです。
(Cache-Controlヘッダーや、プロキシ、CDNの利用)

HTTP/1.1 200 OK
+ Cache-Control: public, max-age=3600
Content-Type: application/json
{
  "id": 1,
  "name": "Product A",
  "price": 1000
}

原則④. Uniform Interface

  • APIの設計は統一されたルールに基づくべきであり、一貫性を持たせましょう
  • 適切なHTTPメソッド(GET, POST, PUT, DELETE)を利用しましょう
  • リソース指向(Resource-Oriented)で設計し、URIは「名詞」を使いましょう
🆖 アンチパターン(リソース名に動詞を含む/メソッドが曖昧、POSTなのかGETなのかわからない)
POST /api/getUser
Content-Type: application/json
{
  "id": 1
}

🆗 デザインパターン(RESTfulな設計)

GET /api/users/1
POST /api/users
PUT /api/users/1
DELETE /api/users/1

直感的ですよね。

原則⑤. Layered System

  • APIの設計はレイヤー(層)を意識し、それぞれの役割を分離する
  • 各レイヤーの役割が明確(凝集性に富んでる)
  • クライアントはシステムの内部構造を知らなくてもよい

※ ここは特にですが、"APIの設計思想"ではなく"システムの設計思想"の原則と理解したほうが飲み込みやすいかもです。

🆖 アンチパターン(レイヤー分離がない)

// ビジネスロジックとデータベースアクセスが混在
$user = $_POST['user'];
$db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $db->prepare("INSERT INTO users (name) VALUES (:name)");
$stmt->bindParam(':name', $user);
$stmt->execute();
header('Content-Type: application/json');
echo json_encode(['message' => 'User created']);

🆗 デザインパターン(レイヤー構造の具体例)

  • プレゼンテーション
  • ユースケース
  • リポジトリ
  • インフラ
  • etc...

原則⑥. Code on Demand

※ この項目は任意です。

  • クライアントは必要に応じて、サーバーからスクリプトやコードを受け取り、実行できるべき
  • クライアントの動作を動的に変更し、柔軟性を高められる

🆖 アンチパターン(クライアントがすべてのロジックを保持)

クライアントが動的にバリデーションスクリプトを取得.js
function validateInput(value) {
    return value.length > 5; // 長さチェックのみ
}

document.getElementById("input").addEventListener("input", (event) => {
    console.log(validateInput(event.target.value));
});

🆗 デザインパターン(サーバーからバリデーションルールを提供)

サーバーがバリデーションルールを提供.php
header('Content-Type: application/javascript');
echo 'function validateInput(value) { return value.length > 8; }';
クライアントがサーバーからバリデーションスクリプトを取得.js
fetch('/api/validation-rules.js')
  .then(response => response.text())
  .then(script => eval(script));

document.getElementById("input").addEventListener("input", (event) => {
    console.log(validateInput(event.target.value));
});

// プロジェクトによってはクライアント+サーバーどちらもバリデーションかけるパターンも多いと思いますが一例です

まとめ

今回は、RESTの原則について私なりの理解をまとめてみました。

やっぱり例があるとわかりやすいですね。

参考

https://www.mulesoft.com/jp/api/rest/what-is-rest-api-design

https://zenn.dev/aiq_dev/articles/48100d5b3f13fe

https://aws.amazon.com/jp/what-is/restful-api

https://speakerdeck.com/nagix/xian-chang-deyi-li-tuapidezain

https://zenn.dev/wsuzume/articles/280ba652dc4fc7

余談

知らなかったんですが、"アンチパターン" の対義語は "デザインパターン" なんですね。

Discussion

ログインするとコメントできます