0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CRUDのAPIにPUTは要らないのでは説

Last updated at Posted at 2022-04-13

はじめに

データベースのテーブル単位でCRUDのAPIを作成している中で、「別にこれPUT要らなくね?」となったのでそのケースについて書いていきます。
「CRUDのAPI」というと複雑で大きな処理も考えられると思いますが、シンプルなレコードの読み書きレベルの想定で書いていると思ってください。

CRUDのAPIにPUTは要らないのでは説

CRUDのAPIの設計をどうしようか検討している時に事例を調べてみると、よく下記のような例が見つかります。

[POST]   /data      ← 作成(C)
[GET]    /data/:id  ← 読取(R)
[PUT]    /data/:id  ← 更新(U)
[DELETE] /data/:id  ← 削除(D)

うん、代表的なRestAPIって感じの綺麗さでいいですね。ここまでは分かります。

でもこれで作っていくと「冪等ってなんだ…?」問題と「コーディング上の都合」問題にぶち当たることになり、PUTはやめてPOSTでいいんじゃないかという気がしてきます。

これから2点、PUT要らない説の話をするので「それはおかしいやろ😠」という内容があればコメントにてご指摘ください。

「冪等ってなんだ…?」問題

PUTの定義を調べると「冪等(べきとう)」でなければいけないと書かれています。
https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/PUT

つまりCRUDのU、更新は送信値で上書きする処理なので何度やっても変わらないから冪等であり、PUTが適しているということですね。これも分かります。
ただ実際のAPIにおいては単純な話で済まないことが多く、更新のAPIには下記のような副作用があることがあります。

  • レコードに更新日時を設定
  • 更新ログの追加
  • 重要なデータの場合はメール等の通知

上記のような副作用がある時にも「冪等」と言えるかというと多分厳しくて、じゃあそれはPOSTにするべきじゃないか、となります。
すると更新のAPIがデータの重要度によってPUTだったりPOSTだったりまちまちになってしまい、使い手側がいちいち「このデータの更新APIはどっちだっけ?」となるのが嫌なので結局「どちらかで統一しようよ」という話になる気がします。
※ここで使い手が都度確認するべきで別に統一しなくても良くない?という話になるならこの話は以上です😞

そこでPOSTとPUTのどっちに統一する?といった話になるんですが、そもそもPUTが冪等であって嬉しいことの1つに「呼ぶ度にリソースが増えたりしない何度呼んでも問題ないAPI」として安心して使えることだと思うんですよね。
ここで実は裏でちゃんと副作用があって呼ぶ度にどんどんリソースが変わってます/増えてます、となると使う側からすると「冪等って言ったじゃん😩」になるのでPUTで統一するのはよろしくない気がします。
(もちろんAPIのマニュアルを書くべき&読むべきというのはそれはそうなんですが名が体を表していないのは問題なので)

すると結局更新はPOSTで統一するのが使う側も細かいこと考えずに済むので良いような気がします。

コーディング上の都合

これは言語などにもよると思うんですが、自分はよくNode.js+expressの環境でコーディングしていて「PUTじゃなくてPOSTのが楽なんだよなあ」となります。
結局CRUDのCもUも「Write」で、コーディング上はCRUDの4つに分かれているよりRead/Write/Deleteの方が扱いやすい気がするんですよね。
具体的にはCRUDのAPIを作っている時は下記のようなコードで書きたくなります。

// Read
app.get('/data/:id', (req, res) => {
  let data = Data.getById(req.params.id);  // URLの:idでデータを取得
  res.json(data);                          // JSONで返す
});

// Write
app.post('/data(/:id)?', (req, res) => {
  // idがあるかどうかで新規かどうか分岐
  let data = req.params.id ? Data.getById(req.params.id) : new Data();

  /*** dataに値を設定する処理 ***/

  // 保存
  data.save();
  res.json(data.id);
});

// Delete
app.delete('/data/:id', (req, res) => {
  let data = Data.getById(req.params.id);
  data.delete();
  res.json(data.id);
});

ここでCとUがPOSTとPUTに分かれてるとめんどくさいんですよ。いや別にロジックを外にまとめればいいだけの話でそんなめんどくさいってほどじゃないんですけど。
もちろん新規と更新で権限チェックだとかそういう箇所が違うこともあるでしょうけど、複雑なものじゃない限りこのパターンの方が楽なんじゃないかなあと思っています。

最後に

もちろん最初に書いた通りこれはテーブル単位でCRUDのAPIを作るシンプルなケースを想定して書いているので、当てはまらない場合もいっぱいあると思います。ただ「みんなCRUDと対応した4つのメソッド使ってるから自分もそうしよう」みたいな状態になっているなら一考の価値はあるかなーと思いこの記事を書きました。

あとみんながPUTのどういうところにメリットを感じて使っているのかが分からなかったのでこの記事によってツッコミをもらいたいというのもあります。PUTを使うと何が嬉しいのか補足できる方は教えてください🙏

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?