Skip to content

hono/vercel handle() return type is incompatible with Next.js 15 App Router route handler types #4877

@AliiiBenn

Description

@AliiiBenn

What version of Hono are you using?

4.7.x

What runtime/platform is your app running on? (with version if possible)

Next.js 15 (App Router), Node.js 20+

What steps can reproduce the bug?

  1. Create a Next.js 15 project with App Router.
  2. Install Hono: pnpm add hono.
  3. Create a catch-all route handler at app/api/[[...route]]/route.ts:
// app/api/[[...route]]/route.ts
import { Hono } from "hono";
import { handle } from "hono/vercel";

const app = new Hono().basePath("/api");

app.get("/hello", (c) => c.json({ message: "Hello, World!" }));

export const GET = handle(app);
export const POST = handle(app);
export const PUT = handle(app);
export const PATCH = handle(app);
export const DELETE = handle(app);
export const OPTIONS = handle(app);
  1. Run tsc --noEmit in the Next.js project root.

TypeScript reports the following errors for every exported method:

app/api/[[...route]]/route.ts:8:14
error TS2344: Type '{ __tag__: "GET"; __param_position__: "first"; __param_type__: Hono<BlankEnv, BlankSchema, "/">; }' does not satisfy the constraint 'ParamCheck<NextRequest | Request>'.
  Types of property '__param_type__' are incompatible.
    Type 'Hono<BlankEnv, BlankSchema, "/">' is not assignable to type 'NextRequest | Request'.

error TS2344: Type '{ __tag__: "GET"; __return_type__: (req: Request) => Response | Promise<Response>; }' does not satisfy the constraint '{ __tag__: "GET"; __return_type__: Response | void | never | Promise<Response | void | never>; }'.
  Types of property '__return_type__' are incompatible.
    Type '(req: Request) => Response | Promise<Response>' is not assignable to type 'Response | void | never | Promise<Response | void | never>'.

The same pair of errors is emitted for POST, PUT, PATCH, DELETE, and OPTIONS.

What is the expected behavior?

tsc --noEmit should pass without errors. Assigning handle(app) to a Next.js 15 App Router route export should be type-safe without requiring any type assertions or suppression comments.

What do you see instead?

Two categories of type errors are produced for every HTTP method export:

1. Return type mismatch

handle() is typed as returning (req: Request) => Response | Promise<Response>.

Next.js 15's generated route constraint (__return_type__) expects Response | void | Promise<Response | void>. Because the void union member is absent from Hono's return type, TypeScript rejects the assignment.

2. First parameter type mismatch

Next.js 15 validates the first parameter type via a ParamCheck constraint and expects NextRequest | Request. Hono's handle() only declares Request, which fails the constraint.

The underlying handle implementation in hono/vercel:

var handle = (app) => (req) => {
  return app.fetch(req);
};

works correctly at runtime — the type errors are purely a TypeScript-level incompatibility introduced by the stricter route handler type constraints added in Next.js 15.

Workaround

Cast the result of handle() to suppress the errors:

import type { NextRequest } from "next/server";

type NextHandler = (
  req: NextRequest | Request,
  ctx?: unknown
) => Response | Promise<Response>;

export const GET = handle(app) as unknown as NextHandler;

Or use // @ts-ignore on each export line.

Neither option is acceptable as a long-term solution since they lose type safety entirely.

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions