◆ クライアント側とサーバ側の両方がベストかも
  ◆ サーバ側は必須
  ◆ 個別に入力ごとにチェックしたりをサーバに送るとムダな負荷
  ◆ クライアントでやっておけばサーバのレスポンスに詳細なエラーを出さなくていい
◆ 両方でやっても処理の共通化は楽じゃない
◆ UI 都合のフォームと実際にサーバで受け取って処理するデータのフォーマットが違う
  ◆ 共通にならない場合も結構ある
◆ API 化してるとサーバとクライアントでチェックする内容や目的が別になる

ウェブでサーバに送られてくるデータを何もチェックせずに使うとセキュリティ面などで問題になるので validation を行います
でも実際にどこでどういうふうにやるのがいいのか結構迷うものです
楽でいい方法が欲しいものです

クライアントとサーバ

まず クライアントかサーバかですが サーバ側は必須です
いくらクライアントでチェックする JavaScript 処理を入れても JavaScript を動かさないとかコマンドラインツールから送信すれば回避できますからね

だからといってサーバのみは利便性で劣ります
昔ながらのだと フォームを POST したときにエラーがあればもう一度フォームが表示されてエラー箇所が目立ってるようなのです
フォームの入力が消えてると不便なのでサーバ側でユーザ入力を設定した HTML を返すなど実装がすごく面倒です
さらに ナビゲーションが発生するのでユーザ的にも嬉しくない方法です
なので今どきこの方法は取ろうとは思えません

もう少し改善すると POST をバックグラウンドで送信します
fetch で送信して エラーがあれば JSON で受け取ったエラー情報を基に DOM を書き換えてエラーを通知します
エラーがなければリダイレクト先を受け取ってそこへリダイレクトします
fetch の POST で保存まで行います
保存後は JavaScript でのリダイレクトなので SPA にも対応できます
とりあえずはこれでもいいかなと思ってます

両方で

問題点があるとすれば POST するのが保存を兼ねるのでチェックだけを行えないことや 全部をまとめてチェックするので部分部分を入力時にチェックできないことです
中には入力中にエラーが色々でてくると邪魔という人もいますが 後からエラーと言われて戻ってくるよりも入力中にわかったほうが便利だと思います
これの解決用に サーバにチェックだけするモードと 1 項目だけをチェックする機能を追加できます
しかし それだとサーバへのリクエスト数が増えて負荷が増えそうです
最終的に保存時にはサーバでチェックするので こういうことをするならクライアント側で JavaScript によるチェックで十分な気がします

また サーバのチェックでユーザに通知するための情報を返すと ブラウザを通さない非正規なリクエストでも丁寧なエラーを返してしまいます
ブラウザ内の JavaScript でチェックしておけば サーバに送信する段階ではエラーはないはずなのでサーバ側ではデータにエラーがあれば詳細は伝えず 400 エラーだけにもできます
あとこの方法なら保存時は fetch じゃなくて フォームで POST してもそこまで困りません

結局 サーバとクライアントのどっちでも validation 処理があったほうが良さそうということになりました
そうなると同じチェック処理をクライアント側とサーバ側の 2 箇所に用意するのが手間です
サーバ側に Node.js を使うなら JavaScript の処理を使いまわして 他の言語なら JSON でルール定義だけして validation 処理は同じものを実装するとかも考えました
ですが 複雑な処理だとやっぱり大変ですし クライアント側のチェックはあくまでフォームの中でやりたいのに対して サーバで受け取るのはフォーマット済みだったりでデータのフォーマットが違います
validation 処理の共通化だけでお手軽に解決できるものでもなかったです

クライアントとサーバで異なる例

例えばこういうのです
  • パスワードの確認
  • 日時や 16 進数の入力
  • 電話やメールなどの分割入力

パスワードは確認のため 2 回入力することが一般的です
ですがサーバに両方を送る必要はないです
あくまでユーザの入力ミスを防ぐ補助機能なので JavaScript の処理で一致確認するだけで十分です
確認して問題なければ一致したものを 1 つ送るだけで良いです

同じくパスワードに使える文字種の制限もサーバ側にはいらないと思います
ブラウザでは●●●と表示されて基本は半角文字しか入力できません
ですがコピペすれば日本語も入力できます
コピペで意図せず日本語が入っていると後からログインできないトラブルのもとになりそうなのでブラウザ上では日本語が入ってるとエラーを出したほうが親切です
しかし正規の画面を通さず API を直接リクエストしてくるユーザには日本語で登録できても特に問題はないと思います

他にもユーザに見せる都合で日時を 「yyyy/MM/dd HH:mm:ss」 形式で入力させてもサーバへの送信やサーバ側での処理では UNIX タイムスタンプの数値型ということもあります
日時以外にも 16 進数での入力も文字列で 16 進数として保存せず 10 進数の数値型で保持するほうが多いと思います
これらはブラウザ側でのみ 日時フォーマットに一致するかや 16 進数で使える文字のみを使ってるかのチェックが必要です

変換というほどではないですが 電話で「□-□-□」やメールで「□@□」みたいに input が分割されてる UI を見かけます
UI のみの都合なので分割されたままサーバに送ったり DB に保存しても無駄が多いです
結合してサーバに送るので ブラウザ上とは異なる validation 処理が必要です

という感じでクライアント側とサーバ側の似てるけど微妙に違う値の validation 処理は面倒です

API だと

また 最近は SPA にしてサーバは API のみにすることが多いです
その場合は記事だったりユーザだったりのリソースに対して作成・編集・削除みたいな API を先に用意しておいて それをブラウザから呼び出す感じです
そうなるとクライアント側とサーバ側の validation 処理はかなり違うものになって来ることが多くて 共通化はしないほうがいいようにも思えてきました
画面(フォーム)が先にあってそこ専用の API を作るならともかく 事前にサーバ側で作っておく API だと 基本は権限とデータとして正しいかのチェックのみになります
その画面の機能では設定できないはずの値でも データ的に設定可能なものなら設定できてしまいます
なので validation 処理的にはクライアント側では画面の項目として正しいかになってサーバ側のデータとして正しいかとは別物になります

例えば簡易設定画面だと true/false の 2 値なのでチェックボックスにしても 詳細設定だともっと色々選べて select になってるならデータ的には enum 型です
UI がチェックボックスでも API に送信するのは enum 値になります
他にも簡易設定画面だと API に送れば設定できる項目でも 画面に設定項目を出さずに送らない項目もあります

ここまでデータの違いや validation 目的に違いがあれば別のものと考えて 2 パターン作ればいいとおもうのですが 結局手間がかかるのに変わりないんですよね