Skip to content

Commit

Permalink
fix(zod-validator): support for case-insensitive headers target val…
Browse files Browse the repository at this point in the history
…idation (#860)
  • Loading branch information
askorupskyy authored Dec 13, 2024
1 parent 27ff98f commit 803f011
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-clocks-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hono/zod-validator': patch
---

Case-insensitive Zod schemas for headers
24 changes: 20 additions & 4 deletions packages/zod-validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse, Input } from 'hono'
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
import { validator } from 'hono/validator'
import type { z, ZodSchema, ZodError } from 'zod'
import { ZodObject, type ZodError, type ZodSchema, type z } from 'zod'

export type Hook<
T,
Expand Down Expand Up @@ -46,10 +46,26 @@ export const zValidator = <
): MiddlewareHandler<E, P, V> =>
// @ts-expect-error not typed well
validator(target, async (value, c) => {
const result = await schema.safeParseAsync(value)
let validatorValue = value

// in case where our `target` === `header`, Hono parses all of the headers into lowercase.
// this might not match the Zod schema, so we want to make sure that we account for that when parsing the schema.
if (target === 'header' && schema instanceof ZodObject) {
// create an object that maps lowercase schema keys to lowercase
const schemaKeys = Object.keys(schema.shape)
const caseInsensitiveKeymap = Object.fromEntries(
schemaKeys.map((key) => [key.toLowerCase(), key])
)

validatorValue = Object.fromEntries(
Object.entries(value).map(([key, value]) => [caseInsensitiveKeymap[key] || key, value])
)
}

const result = await schema.safeParseAsync(validatorValue)

if (hook) {
const hookResult = await hook({ data: value, ...result, target }, c)
const hookResult = await hook({ data: validatorValue, ...result, target }, c)
if (hookResult) {
if (hookResult instanceof Response) {
return hookResult
Expand Down
30 changes: 30 additions & 0 deletions packages/zod-validator/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,33 @@ describe('Only Types', () => {
type verify = Expect<Equal<Expected, Actual>>
})
})

describe('Case-Insensitive Headers', () => {
it('Should ignore the case for headers in the Zod schema and return 200', () => {
const app = new Hono()
const headerSchema = z.object({
'Content-Type': z.string(),
ApiKey: z.string(),
onlylowercase: z.string(),
ONLYUPPERCASE: z.string(),
})

const route = app.get('/', zValidator('header', headerSchema), (c) => {
const headers = c.req.valid('header')
return c.json(headers)
})

type Actual = ExtractSchema<typeof route>
type Expected = {
'/': {
$get: {
input: {
header: z.infer<typeof headerSchema>
}
output: z.infer<typeof headerSchema>
}
}
}
type verify = Expect<Equal<Expected, Actual>>
})
})

0 comments on commit 803f011

Please sign in to comment.