diff --git a/packages/zod-openapi/README.md b/packages/zod-openapi/README.md index b0223b12..6bbe75da 100644 --- a/packages/zod-openapi/README.md +++ b/packages/zod-openapi/README.md @@ -286,10 +286,25 @@ const client = hc('http://localhost:8787/') ## Limitations +### Combining with `Hono` + Be careful when combining `OpenAPIHono` instances with plain `Hono` instances. `OpenAPIHono` will merge the definitions of direct subapps, but plain `Hono` knows nothing about the OpenAPI spec additions. Similarly `OpenAPIHono` will not "dig" for instances deep inside a branch of plain `Hono` instances. If you're migrating from plain `Hono` to `OpenAPIHono`, we recommend porting your top-level app, then working your way down the router tree. +### Header keys + +Header keys that you define in your schema must be in lowercase. + +```ts +const HeadersSchema = z.object({ + // Header keys must be in lowercase, `Authorization` is not allowed. + authorization: z.string().openapi({ + example: 'Bearer SECRET', + }), +}) +``` + ## References - [Hono](https://hono.dev/) diff --git a/packages/zod-openapi/package.json b/packages/zod-openapi/package.json index 723e5e7a..98f81d92 100644 --- a/packages/zod-openapi/package.json +++ b/packages/zod-openapi/package.json @@ -42,6 +42,7 @@ "zod": "3.*" }, "devDependencies": { + "@hono/zod-validator": "^0.1.11", "hono": "^3.9.1", "zod": "^3.22.1" }, diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index de304784..c7fc9404 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -33,31 +33,20 @@ describe('Basic - params', () => { const ParamsSchema = z.object({ id: z .string() - .transform((val, ctx) => { - const parsed = parseInt(val) - if (isNaN(parsed)) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Not a number', - }) - return z.NEVER - } - return parsed - }) + .min(4) .openapi({ param: { name: 'id', in: 'path', }, - example: 123, - type: 'integer', + example: '12345', }), }) const UserSchema = z .object({ - id: z.number().openapi({ - example: 123, + id: z.string().openapi({ + example: '12345', }), name: z.string().openapi({ example: 'John Doe', @@ -68,6 +57,13 @@ describe('Basic - params', () => { }) .openapi('User') + const HeadersSchema = z.object({ + // Header keys must be in lowercase + authorization: z.string().openapi({ + example: 'Bearer SECRET', + }), + }) + const ErrorSchema = z .object({ ok: z.boolean().openapi({ @@ -81,6 +77,7 @@ describe('Basic - params', () => { path: '/users/{id}', request: { params: ParamsSchema, + headers: HeadersSchema, }, responses: { 200: { @@ -136,17 +133,21 @@ describe('Basic - params', () => { }) it('Should return 200 response with correct contents', async () => { - const res = await app.request('/users/123') + const res = await app.request('/users/12345', { + headers: { + Authorization: 'Bearer TOKEN', + }, + }) expect(res.status).toBe(200) expect(await res.json()).toEqual({ - id: 123, + id: '12345', age: 20, name: 'Ultra-man', }) }) it('Should return 400 response with correct contents', async () => { - const res = await app.request('/users/abc') + const res = await app.request('/users/123') expect(res.status).toBe(400) expect(await res.json()).toEqual({ ok: false }) }) @@ -162,7 +163,7 @@ describe('Basic - params', () => { User: { type: 'object', properties: { - id: { type: 'number', example: 123 }, + id: { type: 'string', example: '12345' }, name: { type: 'string', example: 'John Doe' }, age: { type: 'number', example: 42 }, }, @@ -181,11 +182,17 @@ describe('Basic - params', () => { get: { parameters: [ { - schema: { type: 'integer', example: 123 }, + schema: { type: 'string', example: '12345', minLength: 4 }, required: true, name: 'id', in: 'path', }, + { + schema: { type: 'string', example: 'Bearer SECRET' }, + required: true, + name: 'authorization', + in: 'header', + }, ], responses: { '200': {