Skip to content

Commit 803f011

Browse files
authoredDec 13, 2024··
fix(zod-validator): support for case-insensitive headers target validation (#860)
1 parent 27ff98f commit 803f011

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed
 

‎.changeset/odd-clocks-sin.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hono/zod-validator': patch
3+
---
4+
5+
Case-insensitive Zod schemas for headers

‎packages/zod-validator/src/index.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse, Input } from 'hono'
1+
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
22
import { validator } from 'hono/validator'
3-
import type { z, ZodSchema, ZodError } from 'zod'
3+
import { ZodObject, type ZodError, type ZodSchema, type z } from 'zod'
44

55
export type Hook<
66
T,
@@ -46,10 +46,26 @@ export const zValidator = <
4646
): MiddlewareHandler<E, P, V> =>
4747
// @ts-expect-error not typed well
4848
validator(target, async (value, c) => {
49-
const result = await schema.safeParseAsync(value)
49+
let validatorValue = value
50+
51+
// in case where our `target` === `header`, Hono parses all of the headers into lowercase.
52+
// this might not match the Zod schema, so we want to make sure that we account for that when parsing the schema.
53+
if (target === 'header' && schema instanceof ZodObject) {
54+
// create an object that maps lowercase schema keys to lowercase
55+
const schemaKeys = Object.keys(schema.shape)
56+
const caseInsensitiveKeymap = Object.fromEntries(
57+
schemaKeys.map((key) => [key.toLowerCase(), key])
58+
)
59+
60+
validatorValue = Object.fromEntries(
61+
Object.entries(value).map(([key, value]) => [caseInsensitiveKeymap[key] || key, value])
62+
)
63+
}
64+
65+
const result = await schema.safeParseAsync(validatorValue)
5066

5167
if (hook) {
52-
const hookResult = await hook({ data: value, ...result, target }, c)
68+
const hookResult = await hook({ data: validatorValue, ...result, target }, c)
5369
if (hookResult) {
5470
if (hookResult instanceof Response) {
5571
return hookResult

‎packages/zod-validator/test/index.test.ts

+30
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,33 @@ describe('Only Types', () => {
339339
type verify = Expect<Equal<Expected, Actual>>
340340
})
341341
})
342+
343+
describe('Case-Insensitive Headers', () => {
344+
it('Should ignore the case for headers in the Zod schema and return 200', () => {
345+
const app = new Hono()
346+
const headerSchema = z.object({
347+
'Content-Type': z.string(),
348+
ApiKey: z.string(),
349+
onlylowercase: z.string(),
350+
ONLYUPPERCASE: z.string(),
351+
})
352+
353+
const route = app.get('/', zValidator('header', headerSchema), (c) => {
354+
const headers = c.req.valid('header')
355+
return c.json(headers)
356+
})
357+
358+
type Actual = ExtractSchema<typeof route>
359+
type Expected = {
360+
'/': {
361+
$get: {
362+
input: {
363+
header: z.infer<typeof headerSchema>
364+
}
365+
output: z.infer<typeof headerSchema>
366+
}
367+
}
368+
}
369+
type verify = Expect<Equal<Expected, Actual>>
370+
})
371+
})

0 commit comments

Comments
 (0)
Please sign in to comment.