Skip to content

Commit 52aaaf9

Browse files
authored
fix(types): propagate middleware response types to app.on overloads (#4906)
PR #4393 introduced `MergeMiddlewareResponse<M_k>` to capture middleware responses (e.g. `c.json({ error }, 401)` returned from a preceding handler) into the inferred RPC schema. The change was applied to `HandlerInterface` (`app.get/post/...`) but `OnHandlerInterface` was not updated, so the same route declared with `app.on(method, path, ...)` or `app.on(method[], path, ...)` silently dropped middleware responses from the schema. Bring 18 overloads (handler counts 2..10 for both single-method and method[] variants) in line with `HandlerInterface` by introducing M1..M(n-1) middleware type parameters and unioning `MergeMiddlewareResponse<M_k>` into the schema's response type. Tests covering 1, 2, 5, and 9 middlewares for each variant are added under the existing `RPC supports Middleware responses` describe. Follow-up to #4393 and #4865.
1 parent 76d5589 commit 52aaaf9

2 files changed

Lines changed: 672 additions & 106 deletions

File tree

src/types.test.ts

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3498,6 +3498,282 @@ describe('RPC supports Middleware responses', () => {
34983498
type verify = Expect<Equal<Expected, Actual>>
34993499
})
35003500
})
3501+
3502+
describe('Merge responses from app.on handlers, single method', () => {
3503+
test('merge responses from 1 middleware', async () => {
3504+
const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
3505+
3506+
const app = new Hono().on('GET', '/test', middleware, (c) => c.json({ '200': true }, 200))
3507+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3508+
const res = await client.test.$get()
3509+
3510+
if (res.status === 200) {
3511+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3512+
}
3513+
if (res.status === 400) {
3514+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3515+
}
3516+
})
3517+
3518+
test('merge responses from 2 middlewares', async () => {
3519+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3520+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3521+
3522+
const app = new Hono().on('GET', '/test', middleware1, middleware2, (c) =>
3523+
c.json({ '200': true }, 200)
3524+
)
3525+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3526+
const res = await client.test.$get()
3527+
3528+
if (res.status === 200) {
3529+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3530+
}
3531+
if (res.status === 400) {
3532+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3533+
}
3534+
if (res.status === 401) {
3535+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3536+
}
3537+
})
3538+
3539+
test('merge responses from 5 middlewares', async () => {
3540+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3541+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3542+
const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3543+
const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3544+
const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3545+
3546+
const app = new Hono().on(
3547+
'GET',
3548+
'/test',
3549+
middleware1,
3550+
middleware2,
3551+
middleware3,
3552+
middleware4,
3553+
middleware5,
3554+
(c) => c.json({ '200': true }, 200)
3555+
)
3556+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3557+
const res = await client.test.$get()
3558+
3559+
if (res.status === 200) {
3560+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3561+
}
3562+
if (res.status === 400) {
3563+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3564+
}
3565+
if (res.status === 401) {
3566+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3567+
}
3568+
if (res.status === 403) {
3569+
expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3570+
}
3571+
if (res.status === 404) {
3572+
expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3573+
}
3574+
if (res.status === 300) {
3575+
expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3576+
}
3577+
})
3578+
3579+
test('merge responses from 9 middlewares', async () => {
3580+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3581+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3582+
const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3583+
const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3584+
const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3585+
const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3586+
const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3587+
const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3588+
const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
3589+
3590+
const app = new Hono().on(
3591+
'GET',
3592+
'/test',
3593+
middleware1,
3594+
middleware2,
3595+
middleware3,
3596+
middleware4,
3597+
middleware5,
3598+
middleware6,
3599+
middleware7,
3600+
middleware8,
3601+
middleware9,
3602+
(c) => c.json({ '200': true }, 200)
3603+
)
3604+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3605+
const res = await client.test.$get()
3606+
3607+
if (res.status === 200) {
3608+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3609+
}
3610+
if (res.status === 400) {
3611+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3612+
}
3613+
if (res.status === 401) {
3614+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3615+
}
3616+
if (res.status === 403) {
3617+
expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3618+
}
3619+
if (res.status === 404) {
3620+
expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3621+
}
3622+
if (res.status === 300) {
3623+
expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3624+
}
3625+
if (res.status === 201) {
3626+
expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3627+
}
3628+
if (res.status === 202) {
3629+
expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3630+
}
3631+
if (res.status === 203) {
3632+
expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3633+
}
3634+
if (res.status === 301) {
3635+
expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
3636+
}
3637+
})
3638+
})
3639+
3640+
describe('Merge responses from app.on handlers, method[]', () => {
3641+
test('merge responses from 1 middleware', async () => {
3642+
const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
3643+
3644+
const app = new Hono().on(['GET'], '/test', middleware, (c) => c.json({ '200': true }, 200))
3645+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3646+
const res = await client.test.$get()
3647+
3648+
if (res.status === 200) {
3649+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3650+
}
3651+
if (res.status === 400) {
3652+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3653+
}
3654+
})
3655+
3656+
test('merge responses from 2 middlewares', async () => {
3657+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3658+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3659+
3660+
const app = new Hono().on(['GET'], '/test', middleware1, middleware2, (c) =>
3661+
c.json({ '200': true }, 200)
3662+
)
3663+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3664+
const res = await client.test.$get()
3665+
3666+
if (res.status === 200) {
3667+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3668+
}
3669+
if (res.status === 400) {
3670+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3671+
}
3672+
if (res.status === 401) {
3673+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3674+
}
3675+
})
3676+
3677+
test('merge responses from 5 middlewares', async () => {
3678+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3679+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3680+
const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3681+
const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3682+
const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3683+
3684+
const app = new Hono().on(
3685+
['GET'],
3686+
'/test',
3687+
middleware1,
3688+
middleware2,
3689+
middleware3,
3690+
middleware4,
3691+
middleware5,
3692+
(c) => c.json({ '200': true }, 200)
3693+
)
3694+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3695+
const res = await client.test.$get()
3696+
3697+
if (res.status === 200) {
3698+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3699+
}
3700+
if (res.status === 400) {
3701+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3702+
}
3703+
if (res.status === 401) {
3704+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3705+
}
3706+
if (res.status === 403) {
3707+
expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3708+
}
3709+
if (res.status === 404) {
3710+
expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3711+
}
3712+
if (res.status === 300) {
3713+
expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3714+
}
3715+
})
3716+
3717+
test('merge responses from 9 middlewares', async () => {
3718+
const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3719+
const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3720+
const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3721+
const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3722+
const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3723+
const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3724+
const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3725+
const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3726+
const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
3727+
3728+
const app = new Hono().on(
3729+
['GET'],
3730+
'/test',
3731+
middleware1,
3732+
middleware2,
3733+
middleware3,
3734+
middleware4,
3735+
middleware5,
3736+
middleware6,
3737+
middleware7,
3738+
middleware8,
3739+
middleware9,
3740+
(c) => c.json({ '200': true }, 200)
3741+
)
3742+
const client = hc<typeof app>('http://localhost', { fetch: app.request })
3743+
const res = await client.test.$get()
3744+
3745+
if (res.status === 200) {
3746+
expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3747+
}
3748+
if (res.status === 400) {
3749+
expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3750+
}
3751+
if (res.status === 401) {
3752+
expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3753+
}
3754+
if (res.status === 403) {
3755+
expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3756+
}
3757+
if (res.status === 404) {
3758+
expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3759+
}
3760+
if (res.status === 300) {
3761+
expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3762+
}
3763+
if (res.status === 201) {
3764+
expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3765+
}
3766+
if (res.status === 202) {
3767+
expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3768+
}
3769+
if (res.status === 203) {
3770+
expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3771+
}
3772+
if (res.status === 301) {
3773+
expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
3774+
}
3775+
})
3776+
})
35013777
})
35023778

35033779
describe('Handlers returning Promise<void>', () => {

0 commit comments

Comments
 (0)