前回の記事では、Reactでバリデーションを実装してみました。
今回は、バックエンドでExpressにおいてJoiライブラリを使ったバリデーションの実装をしてみたいと思います。
Joiライブラリの基本的な使い方
Joiライブラリは、データのバリデーションを簡単かつ強力に行うことができるツールです。
Joiのドキュメントを参照して、基本的な使い方を見てみます。
まず、Joiライブラリを下記コマンドでインストールします。
npm i joi
下記コードを作成します。
const Joi = require('joi')
const schema = Joi.object({
name: Joi.string().required(),
age: Joi.number().min(0),
location: Joi.string()
})
const { value, error } = schema.validate({name: '鈴木', age: 12, location: '大阪'})
console.log("value: ", value)
console.log("error: ", error)
ここでは、Joiのオブジェクトschema
を定義し、schema
にはname
, age
, location
という三つのプロパティがあります。name
プロパティは文字列型(string
)、かつ省略不可(required
)である。age
プロパティは数字(number
)で、かつ0以上でなければならない(min(0)
)。location
プロパティは文字列型(string
)である。ドキュメントを見ると、ほかにもたくさんの型や数字の範囲を限定するメソッドがあります。これらがバリデーションチェックを楽にしてくれます。
さらにvalidate
メソッドを用いて、引数にオブジェクトを代入し、結果を返します。
このコードを実行すると、下記出力になります。
value: { name: '鈴木', age: 12, location: '大阪' }
error: undefined
この結果からわかる通り、value
には引数で代入したオブジェクトが格納されています。また、入力したオブジェクトはすべて条件を満たしている為エラーも発生していないようです。
次に、schema
のname
プロパティにはrequired
、age
プロパティにはmin(0)
がそれぞれ指定されていますので、name
プロパティを省略した場合とage
プロパティを負の値にした場合をそれぞれ試してみましょう。
nameプロパティを省略した場合
const Joi = require('joi')
const schema = Joi.object({
name: Joi.string().required(),
age: Joi.number().min(0),
location: Joi.string()
})
const { value, error } = schema.validate({ age: 12, location: '大阪' })
console.log("value: ", value)
console.log("error: ", error)
これを実行すると、下記結果が出力されました。
value: { age: 12, location: '大阪' }
error: [Error [ValidationError]: "name" is required] {
_original: { age: 12, location: '大阪' },
details: [
{
message: '"name" is required',
path: [Array],
type: 'any.required',
context: [Object]
}
]
}
想定通りError [ValidationError]: "name" is required
というエラーが出ました。
ageプロパティを負の値にした場合
const Joi = require('joi')
const schema = Joi.object({
name: Joi.string().required(),
age: Joi.number().min(0),
location: Joi.string()
})
const { value, error } = schema.validate({ name: '鈴木', age: -12, location: '大阪' })
console.log("value: ", value)
console.log("error: ", error)
これを実行すると下記結果が出力されました。
value: { name: '鈴木', age: -12, location: '大阪' }
error: [Error [ValidationError]: "age" must be greater than or equal to 0] {
_original: { name: '鈴木', age: -12, location: '大阪' },
details: [
{
message: '"age" must be greater than or equal to 0',
path: [Array],
type: 'number.min',
context: [Object]
}
]
}
これも想定通りError [ValidationError]: "age" must be greater than or equal to 0
というエラーが出ました。
サーバーサイドにおけるバリデーションチェック
では、htmlファイルで簡単にログインフォームを作成して、express内でバリデーションチェックを実装してみたいと思います。
まず、login.html
ファイルを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ログイン</title>
<!-- axiosパッケージをインポートする -->
<script src="https://unpkg.com/axios@1.6.7/dist/axios.min.js"></script>
</head>
<body>
<div>
<label>
username:
<input type="text" class="username">
</label>
</div>
<div>
<label>
password:
<input type="password" class="password">
</label>
</div>
<button>ログイン</button>
<p class="errorMessage"></p>
<script>
const uname = document.querySelector('.username')
const pw = document.querySelector('.password')
const button = document.querySelector('button')
const errorMessage = document.querySelector('.errorMessage')
button.addEventListener('click', async () => {
await axios.post('http://localhost:3000/login', {
username: uname.value,
password: pw.value
}).then(res => {
errorMessage.style.color = 'blue'
errorMessage.innerHTML = res.data
}).catch(error => {
errorMessage.style.color = 'red'
errorMessage.innerHTML = error.response.data.message
})
})
</script>
</body>
</html>
login.html
は、username
とpassword
の値をサーバーに投げかけ、返ってきた結果を受け取って画面に表示するようになっています。
次にサーバーサイドのindex.jsファイルを作成します。
const express = require('express')
const Joi = require('joi')
const cors = require('cors')
const app = express()
const loginSchema = Joi.object({
// username: 文字列、アルファベットor数字、3文字以上、省略不可
username: Joi.string().alphanum().min(3).required(),
// password: 文字列、6文字以上、省略不可
password: Joi.string().min(6).required()
})
app.use(express.json())
app.use(cors())
app.post('/login', (req, res) => {
// req.bodyがloginSchemaの定義に沿っていなければ、変数errorにエラーが入る
const { error } = loginSchema.validate(req.body)
if (error) {
return res.status(400).json({ message: error.details[0].message })
}
res.send('ログイン成功!')
})
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000')
})
index.js
では、loginSchema
を定義し、app.post('/login', ...)
APIでvalidate
メソッドを使ってreq.body
の形式が定義した通りになっているかをチェックしている。error
が存在する場合はエラー内容を返し、存在しない場合は'ログイン成功!'を返す。
完成したものは下記のようになります。