💭

Server Actions(Next13)で使用しているyupのtestで値の重複チェックをする

2023/10/02に公開

前回まで

Server Actionsでreact-hookformとyupを作って簡易なユーザー登録フォームを作った。
https://zenn.dev/nl_kambara/articles/a54612136a4cda

登録を作ったのでログインも作ろうと思い、前回簡素に作っていたため実装していなかったメールアドレスの重複チェックやパスワードのハッシュ化を書いているときにふと思った。
「yupのバリデーションでメールアドレスの重複チェックできるのでは……?」
これができると
バリデーション(フロント) -> バリデーション(バックエンド) -> 返却されたエラーメッセージをフロントで表示
この流れをバリデーション一本で実装できるようになる。
= データ登録の処理が簡略化されるメリットを期待。

やってみた

流れと実装方法

Submitのタイミングでバリデーションを走らせているのでそこで重複チェックをしたい。
yupにtest()があるのでそれを使用する。
https://github.com/jquense/yup#table-of-contents

前回のコード

前回の記事ではバリデーションの内容に重きをおいてなかったので割愛していた。

/schemas/user.ts
import * as yup from 'yup'

export type UserProp = 'name' | 'email' | 'password'
export type User = {
  name: string
  email: string
  password: string
}

export const userSchema = yup.object().shape({
  name: yup.string().required('必須項目です'),
  email: yup.string().email('メールアドレスを入力してください').required('必須項目です'),
  password: yup.string().min(8, '8文字以上で入力してください').required('必須項目です'),
})

実装

重複チェックの関数作成

hooksに置きたいので前回作成したhooksに追記する。

features/user/hooks/create.ts
'use server'
import { hashSync } from 'bcrypt'
import prisma from '~/lib/prisma'

// 追記
export const validationEmail = async (email: string) => {
  'use server'
  const check = await prisma.users.findFirst({
    where: { email: email },
  })

  if (check) return false
  return true
}
// 追記終わり

export const createUser = async (data: { name: string; email: string; password: string }) => {
  'use server'
  console.log(data)
  const hashPath = hashSync(data.password, 10)
  await prisma.user.create({ data: { ...data, password: hashPath } })
  return true
}

yupのtest()falseでエラーにしてくれるので、入力したemailを使用しているユーザーが見つかった場合にfalseを返す形に作成。

yupのコードに追加

schemas/user.ts
import * as yup from 'yup'
import { validationEmail } from '~/features/user/hooks/create'

export type UserProp = 'name' | 'email' | 'password'
export type User = {
  name: string
  email: string
  password: string
}

export const userSchema = yup.object().shape({
  name: yup.string().required('必須項目です'),
  email: yup
    .string()
    .email('メールアドレスを入力してください')
    .required('必須項目です')
    .test('email', 'メールアドレスはすでに使用されています。', (value) => validationEmail(value)),
  password: yup.string().min(8, '8文字以上で入力してください').required('必須項目です'),
})

これで入力したメールアドレスが重複していたら該当のエラーメッセージを出力してくれる。

バリデーション走らせてみる

example@example.comのメールアドレスを重複させる。
一人目作成。
db image

二人目作成
form
同じアドレスを入力してボタンぽち。
valildation image
できるなぁ……。
DBにも追加されなかった。

やってみて

結論:
yupのバリデーションでDB内の値の重複チェックができる。
今までDBの値を比較したりというフロント側でできなかったバリデーションの一本化ができる。
バックエンドの返却値を使ってエラーを表示させる必要もないので、登録処理がシンプルになるという期待通りの結果になった。

余談

簡単にパスワードのハッシュ化もしれっと追加した。
bcryptを使用。

features/user/hooks/create.ts
// 省略

export const createUser = async (data: { name: string; email: string; password: string }) => {
  'use server'
  console.log(data)
  const hashPath = hashSync(data.password, 10)
  await prisma.user.create({ data: { ...data, password: hashPath } })
  return true
}

Discussion