middleware icon indicating copy to clipboard operation
middleware copied to clipboard

either json or form validation with zod-validator

Open ibqn opened this issue 1 year ago • 1 comments

how to to support both form and json body with zValidator?

docs say that multiple validators can be used together i.e.

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

and

validator('form', ...),
validator('json', ...),

can even be used together. However they always through an error, thus neither form or json body is allowed.

ibqn avatar Dec 25 '24 18:12 ibqn

Hi @ibqn

Currently, the Zod Validator does not support validation for either JSON or Form. But, you can write the middleware like the following or middleware to support it.

import { Hono, MiddlewareHandler } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { createMiddleware } from 'hono/factory'

const schema = z.object({
  foo: z.string()
})

const app = new Hono()

const or = (...handlers: MiddlewareHandler[]) =>
  createMiddleware(async (c, next) => {
    let isValid = false
    for (const handler of handlers) {
      const res = await handler(c, next)
      if (res && res.status === 200) {
        isValid = true
        break
      }
    }
    if (!isValid) {
      return c.json(
        {
          valid: false
        },
        400
      )
    }
  })

app.post('/', or(zValidator('form', schema), zValidator('json', schema)), (c) => {
  // @ts-expect-error `or` does not support infer `json`
  const jsonData = c.req.valid('json')
  // @ts-expect-error `or` does not support infer `form`
  const formData = c.req.valid('form')
  return c.json({ jsonData, formData })
})

const jsonRes = await app.request('/', {
  method: 'POST',
  body: JSON.stringify({ foo: 'bar' }),
  headers: {
    'content-type': 'application/json'
  }
})

console.log(jsonRes.status)  // 200
console.log(await jsonRes.json())

const form = new FormData()
form.append('foo', 'bar')

const formRes = await app.request('/', {
  method: 'POST',
  body: form
})

console.log(formRes.status) // 200
console.log(await formRes.json())

yusukebe avatar Dec 29 '24 07:12 yusukebe