diff --git a/.changeset/chatty-phones-rest.md b/.changeset/chatty-phones-rest.md new file mode 100644 index 00000000..bbbd87d3 --- /dev/null +++ b/.changeset/chatty-phones-rest.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +fix: supports named params in nested routes diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 67fb90dc..63a9fc19 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -356,6 +356,7 @@ export class OpenAPIHono< path: SubPath, app?: Hono ): OpenAPIHono> & S, BasePath> { + const pathForOpenAPI = path.replaceAll(/:([^\/]+)/g, '{$1}') super.route(path, app as any) if (!(app instanceof OpenAPIHono)) { @@ -370,13 +371,13 @@ export class OpenAPIHono< case 'route': return this.openAPIRegistry.registerPath({ ...def.route, - path: mergePath(path, def.route.path), + path: mergePath(pathForOpenAPI, def.route.path), }) case 'webhook': return this.openAPIRegistry.registerWebhook({ ...def.webhook, - path: mergePath(path, def.webhook.path), + path: mergePath(pathForOpenAPI, def.webhook.path), }) case 'schema': diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index c7900fa3..e3c08148 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -1228,3 +1228,49 @@ describe('Context can be accessible in the doc route', () => { }) }) }) + +describe('Named params in nested routes', () => { + const root = new OpenAPIHono() + root.doc('/doc', () => ({ + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'my API', + }, + })) + + const sub = new OpenAPIHono() + sub.openapi( + createRoute({ + method: 'get', + path: '/sub/{subId}', + responses: { + 200: { + description: 'Nested response', + }, + }, + }), + (c) => c.json({ params: c.req.param() }) + ) + + root.route('/root/:rootId', sub) + + it('Should return a correct content', async () => { + const res = await root.request('/root/123/sub/456') + expect(res.status).toBe(200) + const data = await res.json() + expect(data).toEqual({ + params: { + subId: '456', + rootId: '123', + }, + }) + }) + + it('Should return a correct path', async () => { + const res = await root.request('/doc') + expect(res.status).toBe(200) + const data = await res.json() + expect(Object.keys(data['paths'])[0]).toBe('/root/{rootId}/sub/{subId}') + }) +})