はじめに
Webアプリを開発する際は, 入力フォームとそのバリデーションを実装することが非常に多いと思います. 皆さんも必要なプロパティが存在するか, 値が適切な範囲に収まっているか, 目的の型にキャストできるかなどをチェックするコードをたくさん書いてきたのではないでしょうか?🤔
一般的にバリデーションは, クライアントサイドでのバリデーションとサーバーサイドでのバリデーションとで別々に実装する必要があるため, バリデーションルールを表現するコードがDRYにならないという問題があります. しかも二度手間で面倒です😱
こういった煩雑さから解放させてくれるのが, 今回紹介するremix-validated-formというライブラリです✨
remix-validated-form
は, 最近流行りのフルスタックフレームワークであるRemix
と組み合わせて利用するライブラリであり, クライアントサイドとサーバーサイドでのバリデーションを一本化してくれる優れものです🔥
zod
やyup
といった有名なバリデーションライブラリと連携して利用することも可能です. zod
を用いる場合は以下のコマンドでインストールできます.
> npm install remix-validated-form @remix-validated-form/with-zod
使い方
今回は以下のような「新規ユーザーの登録」を例として, remix-validated-form
の使い方を紹介していきます. (バリデーションにはzod
を使います)
バリデーターの作成とフォームの実装
remix-validated-form
を使うには, まずバリデーターを作成する必要があります.
import { withZod } from "@remix-validated-form/with-zod";
import { z } from "zod";
export const userRegisterValidator = withZod(
z.object({
age: z.coerce
.number({ invalid_type_error: "年齢は数字で入力してください" })
.safe(),
email: z
.string()
.email({ message: "メールアドレスを正しい形式で入力してください" }),
name: z.string().min(1, { message: "名前が空になっています" }),
})
);
次に, フォームを実装したいページでValidatedForm
コンポーネントを利用します.
このコンポーネントはvalidator
が必須Prop
になっており, フォームに適用したいバリデーターを渡します.
これだけで, フォームに入力中のデータに対してProps
で渡したバリデーターによるクライアントサイドでのバリデーションが実装完了です✨
import { Button, Text, VStack } from "@chakra-ui/react"; // ← スタイリングにChakra UIを使っています
import { ValidatedForm } from "remix-validated-form";
import { userRegisterValidator } from "./userRegisterValidator";
...
const RegisterPage = () => {
return (
<>
...
<Text fontSize="lg" fontWeight="bold" textDecoration="underline">
ユーザ登録
</Text>
<ValidatedForm
validator={userRegisterValidator} // ← バリデーターを設定(必須)
action="/register"
method="POST"
>
<VStack>
<InputFields /> {/* ← 入力欄をまとめたコンポーネント(ユーザー定義) */}
<Button type="submit" colorScheme="purple">
登録する
</Button>
</VStack>
</ValidatedForm>
</VStack>
</>
);
};
export default RegisterPage;
フォームから送信されたデータに対するサーバーサイドでのバリデーションは, validator.validate()
メソッドを実行するだけで簡単に行うことができます.
import { validationError } from 'remix-validated-form'
...
export const action = async ({ request }: ActionArgs) => {
const formData = await userRegisterValidator.validate(
await request.formData()
);
if (formData.error) {
console.log(formData.error);
return validationError(formData.error);
}
const { name, age, email } = formData.data; // ← zodでバリデーション済みのデータを簡単に取得できます
return json({ name, age, email });
};
(※) register.tsx
内のaction
関数はこのエンドポイント(=/register)にリクエストが送信されたときにサーバーサイドで実行される関数です.
バリデーションエラーの取得方法
サーバーサイドでのバリデーションエラーは, formData.error
で簡単に取得できます.
また, validationError
関数をformData.error
に使うことで簡単にエラーレスポンスを生成することができます.
一方で, クライアントサイドのバリデーションエラーは, remix-validated-form
で定義されているuseField
というフックを利用することで取得することができます.
例として, useField
によるエラーの取得とアラートの表示は以下のように実装できます.
import { Alert, AlertIcon, HStack, Input,vText } from "@chakra-ui/react";
import { useField } from "remix-validated-form";
export const InputFields = () => {
const nameError = useField("name");
...
return (
...
<HStack>
<Text mr={4}>名前 :</Text>
<Input
name="name"
width="15vw"
backgroundColor="white"
border="1px solid black"
/>
</HStack>
{nameError.error && ( // ← エラーがある場合は, アラートコンポーネントを表示する
<Alert status="error" width="20vw">
<AlertIcon />
{nameError.error}
</Alert>
)}
...
);
};
バリデーション実行例
クライアントサイド
実際にバリデーションエラーが起きる例として, メールアドレスに不適切な値を入力してみました.
その結果, ブラウザ上では下の画像のように適切なエラーメッセージが表示されました.
一方で, ターミナルでRemix
サーバの出力を見てもフォーム送信のログが出力されていないことから, クライアントサイドでのバリデーションが実行され, 通過しなかったためにリクエストが送信されていないことが確認できます✨
)
※ちなみに, バリデーションをパスしたデータがフォームによって送信された場合は以下のような出力になります.
サーバーサイド
サーバーサイドでのバリデーションを確認するため, 先ほどと同じエラーが起きるデータをcurl
を使って送信しました.
同様にターミナルでRemix
サーバの出力を確認すると, クライアントサイドで表示されたエラーメッセージと同じエラーが出力されていることがわかります.
レスポンスのステータスコードは422なのですが, このステータスコードのレスポンスメッセージはUnprocessable Entity
なので, 送信された内容の理解はできるものの正しく処理できなかったことを表しています!!
まとめ
バリデーターを一つ定義するだけでクライアントサイドとサーバーサイドの両方で一貫したバリデーションを行うことがremix-validated-form
を使えば簡単に実現できます.
また, zod
やyup
といった有名なバリデーションライブラリとの連携を想定して作られているので, 利用経験のあるバリデーションライブラリをそのまま使うことができます.
さらに, クライアントサイドではValidatedForm
にバリデーターをProps
として渡すだけでフォームにバリデーションを設定でき, useField
を使って直感的に対象のフィールドのエラーを取得できます. サーバーサイドではvalidator.validate()
を実行するだけでバリデーションを実行できるうえに, validationError
関数をformData.error
に使うことで簡単にエラーレスポンスを生成できます. こういった諸々の操作が簡単であることも魅力に感じています.
Remix
を使ってWebアプリを開発している方やRemix
に興味があってこれから触ってみたい方に是非おすすめしたいライブラリです!!
何か不明な点などがあれば, 遠慮なくコメントしてください😊