本文へ移動

バリデーション ​

Hono は、非常に薄いバリデーターのみを提供します。 ただし、サードパーティのバリデーターと組み合わせると、強力な機能を実現できます。 また、RPC 機能を使うと、型を通じてクライアントと API の仕様を共有できます。

手動のバリデーター ​

まず、サードパーティのバリデーターを使わず、入力値を検証する方法を紹介します。

hono/validator から validator をインポートします。

ts
import { validator } from 'hono/validator'

フォームデータを検証するには、第 1 引数に form、第 2 引数にコールバックを指定します。 コールバック内で値を検証し、最後に検証済みの値を返します。 validator はミドルウェアとして使えます。

ts
app.post(
  '/posts',
  validator('form', (value, c) => {
    const body = value['body']
    if (!body || typeof body !== 'string') {
      return c.text('Invalid!', 400)
    }
    return {
      body: body,
    }
  }),
  //...

ハンドラー内では、c.req.valid('form') で検証済みの値を取得できます。

ts
, (c) => {
  const { body } = c.req.valid('form')
  // ... do something
  return c.json(
    {
      message: 'Created!',
    },
    201
  )
}

form のほかに、json、query、header、param、cookie を検証対象にできます。

注意

json や form を検証する場合、リクエストには対応する content-type ヘッダーが必要です(json の場合は Content-Type: application/json など)。なければリクエストボディは解析されず、コールバックには空のオブジェクト({})が渡されます。

次のメソッドでテストする際は、content-type ヘッダーを設定することが重要です。 app.request()。

次のようなアプリケーションがあるとします。

ts
const app = new Hono()
app.post(
  '/testing',
  validator('json', (value, c) => {
    // pass-through validator
    return value
  }),
  (c) => {
    const body = c.req.valid('json')
    return c.json(body)
  }
)

テストは次のように記述できます。

ts
// ❌ this will not work
const res = await app.request('/testing', {
  method: 'POST',
  body: JSON.stringify({ key: 'value' }),
})
const data = await res.json()
console.log(data) // {}

// ✅ this will work
const res = await app.request('/testing', {
  method: 'POST',
  body: JSON.stringify({ key: 'value' }),
  headers: new Headers({ 'Content-Type': 'application/json' }),
})
const data = await res.json()
console.log(data) // { key: 'value' }

注意

header を検証する際は、キーとして小文字の名前を使う必要があります。

Idempotency-Key ヘッダーを検証するには、キーに idempotency-key を使います。

ts
// ❌ this will not work
app.post(
  '/api',
  validator('header', (value, c) => {
    // idempotencyKey is always undefined
    // so this middleware always return 400 as not expected
    const idempotencyKey = value['Idempotency-Key']

    if (idempotencyKey == undefined || idempotencyKey === '') {
      throw new HTTPException(400, {
        message: 'Idempotency-Key is required',
      })
    }
    return { idempotencyKey }
  }),
  (c) => {
    const { idempotencyKey } = c.req.valid('header')
    // ...
  }
)

// ✅ this will work
app.post(
  '/api',
  validator('header', (value, c) => {
    // can retrieve the value of the header as expected
    const idempotencyKey = value['idempotency-key']

    if (idempotencyKey == undefined || idempotencyKey === '') {
      throw new HTTPException(400, {
        message: 'Idempotency-Key is required',
      })
    }
    return { idempotencyKey }
  }),
  (c) => {
    const { idempotencyKey } = c.req.valid('header')
    // ...
  }
)

複数のバリデーター ​

複数のバリデーターを使い、リクエストの異なる部分を検証することもできます。

ts
app.post(
  '/posts/:id',
  validator('param', ...),
  validator('query', ...),
  validator('json', ...),
  (c) => {
    //...
  }
)

Zod の使用 ​

サードパーティのバリデーターの 1 つである Zod を使えます。 サードパーティのバリデーターの利用を推奨します。

npm レジストリからインストールします。

sh
npm i zod
sh
yarn add zod
sh
pnpm add zod
sh
bun add zod

zod から z をインポートします。

ts
import * as z from 'zod'

スキーマを記述します。

ts
const schema = z.object({
  body: z.string(),
})

コールバック関数でスキーマを使って検証し、検証済みの値を返すことができます。

ts
const route = app.post(
  '/posts',
  validator('form', (value, c) => {
    const parsed = schema.safeParse(value)
    if (!parsed.success) {
      return c.text('Invalid!', 401)
    }
    return parsed.data
  }),
  (c) => {
    const { body } = c.req.valid('form')
    // ... do something
    return c.json(
      {
        message: 'Created!',
      },
      201
    )
  }
)

Zod バリデーターミドルウェア ​

Zod バリデーターミドルウェアを使うと、さらに簡単になります。

sh
npm i @hono/zod-validator
sh
yarn add @hono/zod-validator
sh
pnpm add @hono/zod-validator
sh
bun add @hono/zod-validator

続いて、zValidator をインポートします。

ts
import { zValidator } from '@hono/zod-validator'

次のように記述します。

ts
const route = app.post(
  '/posts',
  zValidator(
    'form',
    z.object({
      body: z.string(),
    })
  ),
  (c) => {
    const validated = c.req.valid('form')
    // ... use your validated data
  }
)

Standard Schema バリデーターミドルウェア ​

Standard Schema は、TypeScript のバリデーションライブラリーに共通のインターフェースを提供する仕様です。Zod、Valibot、ArkType のメンテナーが作成し、エコシステムのツールがカスタムアダプターなしで任意のバリデーションライブラリーを扱えるようにします。

Standard Schema バリデーターミドルウェアを使うと、Standard Schema に対応する任意のバリデーションライブラリーを Hono で利用できます。一貫した型安全性を保ちながら、好みのバリデーターを柔軟に選べます。

sh
npm i @hono/standard-validator
sh
yarn add @hono/standard-validator
sh
pnpm add @hono/standard-validator
sh
bun add @hono/standard-validator

パッケージから sValidator をインポートします。

ts
import { sValidator } from '@hono/standard-validator'

Zod の使用 ​

Standard Schema バリデーターで Zod を使えます。

sh
npm i zod
sh
yarn add zod
sh
pnpm add zod
sh
bun add zod
ts
import * as z from 'zod'
import { sValidator } from '@hono/standard-validator'

const schema = z.object({
  name: z.string(),
  age: z.number(),
})

app.post('/author', sValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({
    success: true,
    message: `${data.name} is ${data.age}`,
  })
})

Valibot の使用 ​

Valibot は、モジュール式の設計を採用した Zod の軽量な代替です。

sh
npm i valibot
sh
yarn add valibot
sh
pnpm add valibot
sh
bun add valibot
ts
import * as v from 'valibot'
import { sValidator } from '@hono/standard-validator'

const schema = v.object({
  name: v.string(),
  age: v.number(),
})

app.post('/author', sValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({
    success: true,
    message: `${data.name} is ${data.age}`,
  })
})

ArkType の使用 ​

ArkType は、ランタイムバリデーションに TypeScript に即した構文を提供します。

sh
npm i arktype
sh
yarn add arktype
sh
pnpm add arktype
sh
bun add arktype
ts
import { type } from 'arktype'
import { sValidator } from '@hono/standard-validator'

const schema = type({
  name: 'string',
  age: 'number',
})

app.post('/author', sValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({
    success: true,
    message: `${data.name} is ${data.age}`,
  })
})

MIT ライセンスで公開されています。