RESTFulではない

# はじめに
初めてRESTを使う機会がありRESTFulですか？
という質問があがったが正直よく分からなかった。

今後議論できるように
RESTFulについて、
「RESTFul**ではない**」とはどういうことか批判的アプローチをしたいと思う。

# 「RESTFul**ではない**」７つの理由
 1. リソース指向でない
 2. HTTPメソッドの誤用
 3. ステートフル
 4. 統一インターフェースの欠如
 5. リソースの表現が不適切
 6. 自己記述的メッセージの欠如
 7. HATEOASの欠如

## 1. リソース指向でない
エンドポイントがリソースではなく、動詞やアクションを表現している。
```yaml:リソース指向でない例（抜粋）
  /create_user: #エンドポイント名【Path Item Object】
    POST: # HTTPメソッド
      summary: ユーザを作成する

```
**↑問題点：** エンドポイントが動詞を使用しておりリソース指向でない。
```yaml:リソース指向である例（抜粋）
  /user: #エンドポイント名【Path Item Object】
    POST: # HTTPメソッド
      summary: ユーザを作成する

```
↑エンドポイント名（名詞）とHTTPメソッド（動詞）を組み合わせて表現する。
userをPOSTする。
リソース = 資源・資産（= 物）を意味するので名詞になる。

## 2. HTTPメソッドの誤用
HTTPメソッドを適切に使用していない。
```yaml:HTTPメソッドの誤用例（抜粋）
  /user: #エンドポイント名【Path Item Object】
    GET: # HTTPメソッド
      summary: ユーザを作成する
```
**↑問題点：** リソースを作成するエンドポイントでGETを指定している。
```yaml:HTTPメソッドの正常例（抜粋）
  /user: #エンドポイント名【Path Item Object】
    POST: # HTTPメソッド
      summary: ユーザを作成する
```
↑エンドポイントの動作に沿ったHTTPメソッドを使用する。
userをPOSTする。
エンドポイントの振る舞いに合ったHTTPメソッド。

## 3. ステートフル
サーバーがクライアントの状態を保持している
```yaml:サーバーがクライアントの状態を保持している例（例１）（抜粋）
  /login: #エンドポイント名【Path Item Object】
    POST: # HTTPメソッド
      summary: サーバ側でログインを行う
```

```yaml:サーバーがクライアントの状態を保持している例（例２）（抜粋）
  /user: # エンドポイント名【Path Item Object】
    post: # HTTPメソッド
      summary: ユーザを作成する
      parameters:
        - name: sessionId
          in: query
          required: true
          schema:
            type: string
          description: セッションID
```
**↑問題点：** RESTではステートレス（状態を持たない）であるべきだが、
サーバーがセッションを保持して、クライアントの状態を管理している。
```yaml:サーバーがクライアントの状態を保持しない例（例２）（抜粋）
  /user: # エンドポイント名【Path Item Object】
    post: # HTTPメソッド
      summary: ユーザを作成する
      parameters:
        - name: sessionId
          in: header
          required: true
          schema:
            type: string
          description: セッションID
```
↑ログイン状態を確認するHTTPヘッダーとして渡す。
例１はログイン状態をサーバーで把握することは不適切。
例２はメタ情報として渡すがqueryではなくheaderとして渡すことで
表面的にはRESTFulになる（実際にはサーバー側でセッション状態を保持しているため、本質的にはステートフル）。

## 4. 統一インターフェースの欠如
```yaml:統一インターフェースの欠如例（抜粋）
  /products: # エンドポイント名【Path Item Object】
    get:
      summary: 商品のリストを取得するエンドポイント
    post:
      summary: 新しい商品を作成するエンドポイント
  /products/{id}: # エンドポイント名【Path Item Object】
    get:
      summary: 特定の商品を取得するエンドポイント
    put:
      summary: 特定の商品を更新するエンドポイント
  /products/{id}/archive: # エンドポイント名【Path Item Object】
    post:
      summary: 特定の商品をアーカイブするエンドポイント
```
**↑問題点：** エンドポイントの考え方に一貫性がない。

```yaml:統一インターフェースの非欠如例（抜粋）
  /products: # エンドポイント名【Path Item Object】
    get:
      summary: 商品のリストを取得するエンドポイント
    post:
      summary: 新しい商品を作成するエンドポイント
  /products/{id}: # エンドポイント名【Path Item Object】
    get:
      summary: 特定の商品を取得するエンドポイント
    put:
      summary: 特定の商品を更新するエンドポイント
    patch:
      summary: 特定の商品を部分的に更新するエンドポイント（アーカイブなど）
```
↑商品のアーカイブエンドポイントを/products/{id}のPATCHに変更して一貫性を持たせる。


## 5. リソースの表現が不適切
```yaml:リソースの表現が不適切例（抜粋）
  /users: # エンドポイント名【Path Item Object】
    get:
      summary: ユーザのリストを取得する
      responses:
        '200':
          description: ユーザのリストを取得しました
          content:
            application/xml: # 不適切なデータフォーマット
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    name:
                      type: string
```
**↑問題点：** レスポンスの形式が推奨された形式になっていない。

```yaml:リソースの表現が適切例（抜粋）
  /users: # エンドポイント名【Path Item Object】
    get:
      summary: ユーザのリストを取得する
      responses:
        '200':
          description: ユーザのリストを取得しました
          content:
            application/json: # 適切なデータフォーマット
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    name:
                      type: string
```
↑レスポンスの形式を推奨されているapplication/jsonに変更してAPIの互換性を高める。

## 6. 自己記述的メッセージの欠如
**自己記述的メッセージ（Self-descriptive Messages）：**
メッセージ自体にその内容を理解するために必要な情報が含まれていることを指す。
必要な情報が含まれていることで、
クライアントやサーバーがメッセージの内容を解釈するのに
追加のコンテキストや外部の情報を必要としない。

**自己記述的メッセージの欠如例**
- コメント（summary, description）が書かれていない、誤っている、または内容が不十分
- レスポンスとして呼び出し元に必要な情報（データ・エラー）が不足している
- ステータスコードが適切に使用されていない
  - 例: リソースが見つからない場合に`404 Not Found`を返さず、`200 OK`を返す
- 必要なヘッダー情報が含まれていない
- リクエストやレスポンスのフォーマットが一貫していない

**自己記述的メッセージの非欠如例**
- 分かりやすいコメント（summary, description）が書かれている
- レスポンスとして呼び出し元に必要な情報（データ・エラー）を返される
- 適切なステータスコードが使用されている
- 必要なヘッダー情報が含まれている
- リクエストやレスポンスのフォーマットが一貫している

## 7. HATEOASの欠如
**HATEOAS（Hypermedia as the Engine of Application State）：**
APIのレスポンスにハイパーメディアリンクを含めることで、クライアントが次にどのエンドポイントにアクセスすべきかを示す設計パターン。


```yaml:HATEOASの欠如例（抜粋）
  /user:
    post:
      summary: 新しいユーザを作成するエンドポイント
      responses:
        '201':
          description: ユーザが正常に作成されました
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_name:
                    type: string
                    description: ユーザの名前
```
**↑問題点：** HATEOASが記載されていない。

```yaml:HATEOASの非欠如例（抜粋）
  /user:
    post:
      summary: 新しいユーザを作成するエンドポイント
      responses:
        '201':
          description: ユーザが正常に作成されました
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_name:
                    type: string
                    description: ユーザの名前
                  links:
                    type: array
                    items:
                      type: object
                      properties:
                        rel:
                          type: string
                          description: リンクの関係性
                        href:
                          type: string
                          description: リンク先のURL
                        method:
                          type: string
                          description: HTTPメソッド
                      required:
                        - rel
                        - href
                        - method
```
↑HATEOASとしてlinksを記載する

## 所感
個人の想像ですが、現実的にRESTFulを守れているか否かで考えると以下の整理になります。

 1. リソース指向でない
     - 基本的に起こらないが、設計の初期段階や経験不足のチームでは起こり得る
 2. HTTPメソッドの誤用
     - プロジェクトの特性で起こることがあり、使用するHTTPを制限し意図して本来と異なるHTTPメソッドを使用することがある
 3. ステートフル
     - プロジェクトの特性で起こることがあり、技術や業務的な制約に起因してステートフルになることがある
 4. 統一インターフェースの欠如
     - 基本的に起こらないが、設計の一貫性が保たれていない場合や経験不足のチームでは起こり得る
 5. リソースの表現が不適切
     - 基本的に起こらないが、設計の初期段階や経験不足のチームでは起こり得る
 6. 自己記述的メッセージの欠如
     - 基本的に起こらないが、設計の一貫性が保たれていない場合や経験不足のチームでは起こり得る
 7. HATEOASの欠如
     - プロジェクトの特性で起こることがあり、対応に時間がかかるので省略されることがある

調べてしまうと気軽に「RESTFulです！」って言えなくなりました。
なんとなくですが、
「 1. リソース指向でない」、「 4. 統一インターフェースの欠如」、「 5. リソースの表現が不適切」、「 6. 自己記述的メッセージの欠如」になっていない前提で、
プロジェクトの制約がなく「2. HTTPメソッドの誤用」「3. ステートフル」に反していない場合に「RESTFulです！」と言ってそうな気がしました。

「 7. HATEOASの欠如」については、そもそも欠落している方が多い印象でそもそも我々のRESTFulの前提には含まない！となっている気がします。