ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

Next.jsでバリデーションするなら「Ajv」がいい感じ

ajvはNode.jsとブラウザ用のバリデーションライブラリ。
Typescriptで利用できるバリデーションライブラリといえば、yup, zod, joiあたりが候補に上がるが、ajvを採用した。
理由はバックエンド・クライアント両方で使えること、スキーマファイルを使えること、そしてIE11対応が可能であることが決め手となった。
ajvはスキーマファイルを使ってもいいし、コードで書いてもいいしで使いやすそうであった。

github.com

動作確認環境

  • next: 12.1.0
  • ajv: 8.8.2
  • ajv-formats: 2.1.1

インストール

yarn add ajv

基本的な使い方

1. バリデーションファイルを作成

login.validator.ts

import Ajv from 'ajv'
const ajv = new Ajv()

const schema = {
  type: 'object',
  properties: {
    username: { type: 'string' },
  },
  required: ['username'],
  additionalProperties: true, // username以外のパラメータがあっても許容する
}

export const validate = ajv.compile(schema)

2. バックエンド(Node.js)で利用する

先程作ったvalidateメソッドを呼び出して値を検証するだけ。
api/login.ts

import { validate } from '@/validators/login.validator'

export default function handler(req, res) {
  const isValid = validate(req.body)
  res.status(200).json({ result: isValid })
}

3. クライアント(ブラウザ)で利用する

ブラウザで利用するときも同じ。
pages/users.tsx

import { validate } from '@/validators/login.validator'
import { useRef } from 'react'

const Users = () => {

  const ref = useRef(null)

  const onClick = () => {
    const username = ref.current.value
    const isValid = validate({ username })
  }

  return (
    <div>
      ユーザー名: <input ref={ref} type="text" name="username" />
      <button onClick={onClick}>送信</button>
    </div>
  )
}
export default Users

自作のバリデーションルールを適用

例えば'hoge'という文字列しか受け付けないようなルールを適用したい場合

import Ajv from 'ajv'
const ajv = new Ajv()

+ function isHoge(schema, data) {
+   return data === 'hoge'
+ }
+ 
+ ajv.addKeyword({
+   keyword: 'isHogeValue',
+   validate: isHoge,
+ })

const schema = {
  type: 'object',
  properties: {
+   username: { type: 'string', isHogeValue: isHoge },
  },
  required: ['username'],
  additionalProperties: true,
}

export const validate = ajv.compile(schema)

keywordとvalidatorの名前を同じにしておけばもうちょっとシンプルになる。

import Ajv from 'ajv'
const ajv = new Ajv()

function isHoge(schema, data) {
  return data === 'hoge'
}

ajv.addKeyword({
+   keyword: 'isHoge',
    validate: isHoge,
})

const schema = {
  type: 'object',
  properties: {
+   username: { type: 'string', isHoge },
  },
  required: ['username'],
  additionalProperties: true,
}

export const validate = ajv.compile(schema)

IE11 対応

next.config.jsを下記のようにする。ajv-formatsを使っている場合はajv-formatsも対象にする。

module.exports = {
  webpack(config, options) {
    config.module.rules.push({
      test: /node\_modules\/.*(ajv|ajv-formats)\/.*\.js$/, // ajvをIE11対応させる
      use: [
        options.defaultLoaders.babel,
        {
          loader: 'babel-loader?cacheDirectory',
          options: { presets: ['next/babel'] },
        },
      ],
    })
    return config
  },
}

終わりに

今回はシンプルにコードベースでバリデーションを書いていったが、スキーマファイルを使った例も試したい。
スキーマファイルを使う場合はスキーマファイルを自動生成してajvで使うといったことができると開発体験が良くなりそうだ。