8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Remixでは簡単にバリデーションできます😎

Last updated at Posted at 2023-11-17

はじめに

Webアプリを開発する際は, 入力フォームとそのバリデーションを実装することが非常に多いと思います. 皆さんも必要なプロパティが存在するか, 値が適切な範囲に収まっているか, 目的の型にキャストできるかなどをチェックするコードをたくさん書いてきたのではないでしょうか?🤔

一般的にバリデーションは, クライアントサイドでのバリデーションサーバーサイドでのバリデーションとで別々に実装する必要があるため, バリデーションルールを表現するコードがDRYにならないという問題があります. しかも二度手間で面倒です😱

こういった煩雑さから解放させてくれるのが, 今回紹介するremix-validated-formというライブラリです✨
remix-validated-formは, 最近流行りのフルスタックフレームワークであるRemixと組み合わせて利用するライブラリであり, クライアントサイドとサーバーサイドでのバリデーションを一本化してくれる優れものです🔥
zodyupといった有名なバリデーションライブラリと連携して利用することも可能です. zodを用いる場合は以下のコマンドでインストールできます.

> npm install remix-validated-form @remix-validated-form/with-zod

使い方

今回は以下のような「新規ユーザーの登録」を例として, remix-validated-formの使い方を紹介していきます. (バリデーションにはzodを使います)
前提.png

バリデーターの作成とフォームの実装

remix-validated-formを使うには, まずバリデーターを作成する必要があります.

userRegisterValidator.ts
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で渡したバリデーターによるクライアントサイドでのバリデーションが実装完了です✨

register.tsx
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()メソッドを実行するだけで簡単に行うことができます.

register.tsx
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によるエラーの取得とアラートの表示は以下のように実装できます.

inputFields.tsx
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>
      )}
    ...
  );
};

バリデーション実行例

クライアントサイド

実際にバリデーションエラーが起きる例として, メールアドレスに不適切な値を入力してみました.
その結果, ブラウザ上では下の画像のように適切なエラーメッセージが表示されました.
csv1.png

一方で, ターミナルでRemixサーバの出力を見てもフォーム送信のログが出力されていないことから, クライアントサイドでのバリデーションが実行され, 通過しなかったためにリクエストが送信されていないことが確認できます✨
)csv2.png

※ちなみに, バリデーションをパスしたデータがフォームによって送信された場合は以下のような出力になります.
csv3.png

サーバーサイド

サーバーサイドでのバリデーションを確認するため, 先ほどと同じエラーが起きるデータをcurlを使って送信しました.
ssv1.png

同様にターミナルでRemixサーバの出力を確認すると, クライアントサイドで表示されたエラーメッセージと同じエラーが出力されていることがわかります.
レスポンスのステータスコードは422なのですが, このステータスコードのレスポンスメッセージはUnprocessable Entityなので, 送信された内容の理解はできるものの正しく処理できなかったことを表しています!!
ssv2.png

まとめ

バリデーターを一つ定義するだけでクライアントサイドとサーバーサイドの両方で一貫したバリデーションを行うことがremix-validated-formを使えば簡単に実現できます.
また, zodyupといった有名なバリデーションライブラリとの連携を想定して作られているので, 利用経験のあるバリデーションライブラリをそのまま使うことができます.

さらに, クライアントサイドではValidatedFormにバリデーターをPropsとして渡すだけでフォームにバリデーションを設定でき, useFieldを使って直感的に対象のフィールドのエラーを取得できます. サーバーサイドではvalidator.validate()を実行するだけでバリデーションを実行できるうえに, validationError関数をformData.errorに使うことで簡単にエラーレスポンスを生成できます. こういった諸々の操作が簡単であることも魅力に感じています.

Remixを使ってWebアプリを開発している方やRemixに興味があってこれから触ってみたい方に是非おすすめしたいライブラリです!!
何か不明な点などがあれば, 遠慮なくコメントしてください😊

参考文献

8
5
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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?