fix(zod-openapi): handle nested app base paths in openapi registration (#955)

* feat: handle nested app base paths in openapi registration

* test: add test for nested base paths in OpenAPI schema

* test(zod-openapi): add route without base path

* chore: add changeset

* chore: format
pull/957/head
Lucas 2025-02-04 10:21:56 +01:00 committed by GitHub
parent deae708dde
commit 70a564e268
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 2 deletions

View File

@ -0,0 +1,5 @@
---
'@hono/zod-openapi': patch
---
fix: use nested app base paths in openapi schema

View File

@ -632,13 +632,23 @@ export class OpenAPIHono<
case 'route':
return this.openAPIRegistry.registerPath({
...def.route,
path: mergePath(pathForOpenAPI, def.route.path),
path: mergePath(
pathForOpenAPI,
// @ts-expect-error _basePath is private
app._basePath,
def.route.path
),
})
case 'webhook':
return this.openAPIRegistry.registerWebhook({
...def.webhook,
path: mergePath(pathForOpenAPI, def.webhook.path),
path: mergePath(
pathForOpenAPI,
// @ts-expect-error _basePath is private
app._basePath,
def.webhook.path
),
})
case 'schema':

View File

@ -1156,6 +1156,89 @@ describe('basePath()', () => {
const data = (await res.json()) as any
expect(Object.keys(data.paths)[0]).toBe('/api/message')
})
it('Should add nested base paths to openapi schema', async () => {
const app = new OpenAPIHono()
const v1 = new OpenAPIHono().basePath('/api/v1')
v1.openapi(
{
method: 'get',
path: '/message1',
responses: {
200: {
description: 'Get message',
content: {
'application/json': {
schema: z.object({ message: z.string() }),
},
},
},
},
},
(c) => c.json({ message: 'Hello' })
)
const v2 = new OpenAPIHono().basePath('/api/v2')
v2.openapi(
{
method: 'get',
path: '/message2',
responses: {
200: {
description: 'Get message',
content: {
'application/json': {
schema: z.object({ message: z.string() }),
},
},
},
},
},
(c) => c.json({ message: 'Hello' })
)
app.route('/', v1)
app.route('/', v2)
app.openapi(
{
method: 'get',
path: '/hello',
responses: {
200: {
description: 'Get message',
content: {
'application/json': {
schema: z.object({ message: z.string() }),
},
},
},
},
},
(c) => c.json({ message: 'Hello' })
)
const res1 = await app.request('/api/v1/message1')
expect(res1.status).toBe(200)
const res2 = await app.request('/api/v2/message2')
expect(res2.status).toBe(200)
const json = app.getOpenAPIDocument({
openapi: '3.0.0',
info: {
version: '1.0.0',
title: 'My API',
},
})
const paths = Object.keys(json.paths)
expect(paths).toStrictEqual(['/api/v1/message1', '/api/v2/message2', '/hello'])
expect(paths).not.toStrictEqual(['/message1', '/message2', '/hello'])
})
})
describe('With hc', () => {