はじめに
先日のNext14の発表で大きく話題になった、Server Actionsです。SQLが直接書けるで、いい意味でも悪い意味でも大きく話題になりました。
ただ、別にこれはサーバー側だけで動くコードが書けるということを表しているのであって、その例でただ示しているだけです。別にSQLが書けることはさほど重要ではなく、またこれはサーバーの中で動作するのであって別にブラウザ直にSQL叩いているわけではないです。
Server Actionsはいろいろとメリットはあるとは思うのですが、自分の中では、APIの隠蔽とバリデーションにとてもいいのではと思いました。
従来のNext.jsのフォーム送信
従来はAPIベースでバックエンドとやりとりをしています。Api RouteにAPIを立てて、送信するときにフォームのデータをjsonに変換して送信しています。
今回は名前とメールアドレスを送信するフォームを想定しています。
import { useForm } from 'react-hook-form'
export default function SubmitForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = handleSubmit(async (value) =>
fetch(`/api/user`, {
method: 'POST',
body: JSON.stringify({ ...value }),
}),
)
return (
<form onSubmit={onSubmit}>
<input type='text' {...register('name')} />
<input type='text' {...register('email')} />
<button type="submit">送信</button>
</form>
)
}
こんな感じで大体は、送信時にjsonに変換して、それをApi Routeで作ったApiでそれを受けとってサーバーサイドなりに送信処理をしていました。
このやり方の欠点として、APIが丸見えだという部分です。「これは/api/userにフォーム送信しているんだな」っていうのがユーザーに丸見えです。例えば悪意あるユーザーがこのapiを叩いて、不正なデータを送信するかもしれません。
そんな時に、Server Actions経由でjsonに変換してPOST APIを叩けば、apiが隠蔽されてわかりにくくなります。
"use server";
export const formActions = async (data: FormData) => {
const name = data.get("name") as string;
const email = data.get("email") as string;
await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/user`, {
method: "POST",
body: JSON.stringify({ name, email }),
});
};
"use client"
import { useForm } from 'react-hook-form'
export default function SubmitForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
return (
<form action={formActions}>
<input type='text' {...register('name')} />
<input type='text' {...register('email')} />
<button type="submit">送信</button>
</form>
)
}
これで送信すると、Chrome dev toolsで確認すると、送信しているのはformデータとなり、どこのAPIを通って送っているかわからなくなります。安全性が増しますね。
ただ、サーバー側で使う場合は/api/user
と叩いてもURLがわからないとエラーになるので、.env
ファイルなどに現在のURLを記述しておいて、そこからURLを取得する必要があります。今回はNEXT_PUBLIC_BASE_URLにurlを記述しています。
バリデーション
zodを使えばuseFormと組み合わせるとフロントのバリデーションが簡単に実装できます。
そのzodで作ったschemaを使って、Server Actions側のコードに組み込めばサーバー側でもバリデーションが実装できます。safeParseを使えばバリデーションが行えます。
"use server";
import { schema } from "./_schema";
export const formActions = async (data: FormData) => {
const name = data.get("name") as string;
const email = data.get("email") as string;
const result = schema.safeParse({ name, email });
if (!result.success) {
console.log("Invalid Error", result.error);
return false;
}
await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/user`, {
method: "POST",
body: JSON.stringify({ name, email }),
});
};
フロント側とサーバー側で共通のバリデーション処理を行えるのはとてもメリットだと思いました。不正な操作をしてフォームを送信できたとしても、サーバーで不正な値は弾けるようになりました。もちろんサーバー側の処理なので、ユーザーからはコードは確認できません。
また、Server Actionsは"use server"
をつけないと動かないので、万が一消したとしても「これはサーバーで動く処理なのでだめです」とちゃんとエラーがでます。
完成形のCodeSandboxも置いておきます。
最後に
フロントエンドってめちゃくちゃ情報の更新が早いです。どんどんと新しい技術が出てきます。フロントエンド以外の界隈からすると全く追いついていけないとは思います。
しかし、やってはいけないと僕が思うのは、最新技術を嘲笑って否定することです。やるべきことは、技術をしっかり調査して使うべきか使わざるべきかを判断することです。
今回もServer Actionsが発表されたとき、国内外問わずバカにする発言が数多く見受けられました。そういう方達の発言は一切耳を傾ける必要はありません。大体は何も調べずに上っ面だけみて小馬鹿にしているだけです。でも正しく真正面から批判をしている人の意見は聞くべきです。馬鹿にしていいのはNext.jsのスポンサーだけだと思っています。OSSの技術を馬鹿にしたければちゃんとOSSに寄付しましょう。そうでなければ馬鹿にするのは控えましょう。
Next.js側もServer Actionsという選択肢を増やしただけであって、そのほかの技術を消し去ったわけではありません。なので調べてみて自分のプロジェクトには合わないなと思ったら、無理に使う必要はありません。
みなさんもエンジニアであれば、技術に、エンジニアに敬意をもって開発を行いましょう
参考