npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nextrush/adapter-nextjs

v1.0.0

Published

Mount a NextRush app in a Next.js App Router route handler (Web-standard, App Router only, Next 14/15/16)

Readme

@nextrush/adapter-nextjs

Mount a NextRush app in a Next.js App Router route handler — one function, seven exports, zero request rewriting.

npm version downloads bundle size types ESM only license

| | | | --- | --- | | Purpose | Mount a NextRush Application in a Next.js App Router route handler | | Package type | Adapter | | Status | Beta 🚧 | | Included in nextrush? | ✅ Yes — re-exported as nextrush/nextjs (optional peer, functional installs skip it) | | Support tier | Public — stable surface once out of beta — see ADR-0014 | | Maintenance | Active | | Runtime | Universal — Node · Bun · Deno · Cloudflare (via OpenNext) — anywhere Next.js itself runs | | Requires | Node >=22 · ESM-only · TypeScript >=5.x · Next.js >=14.0.0 (App Router only) | | Introduced | v1.0.0-beta.0 |

Highlights

  • Zero request rewritingctx.path/ctx.url/ctx.raw.req are always the true request; no mount-prefix magic to undo
  • Fully Web-standard — no node:*, process, Buffer, or runtime global; one entry point runs on every host Next.js runs on
  • next is an optional peer — a functional-only nextrush install never resolves it
  • 📦 Bundle: ~4 KB min+gzip

The problem · When to use · Installation · Quick start · Capabilities · Mental model · Common tasks · Project structure · API overview · Options · Compatibility · Troubleshooting · FAQ · Package relationships · Architecture · Resources


The problem

Next.js's App Router lets you drop an API into app/api/.../route.ts — but the moment your API grows past a couple of routes, you're stuck choosing between two bad options:

  • Write everything by hand in route.ts — every method (GET/POST/PATCH/…), every param, every validation check, one giant file per route segment, with no shared middleware, no router, no way to split "list users" and "create user" into separate concerns.
  • Reach for a full API framework that assumes it owns the process — most expect to call listen() and run their own server. Next.js already owns the process; there's no listen() to call, and no way to hand a Request to something that only speaks (req, res).

What you actually want is what Hono popularized: a real app — routers, middleware, a service layer, typed context — built once, the same way regardless of where it runs, then mounted into the route file with one line:

// app/api/[[...route]]/route.ts
import { app } from '@/server/app';
import { handle } from 'nextrush/nextjs';

export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = handle(app);

That's this package's entire job: bridge a real NextRush Application — however you've built it, across however many files — into the seven exports route.ts needs, without rewriting the request or dropping background work. ctx.waitUntil() is silently a no-op under a hand-rolled bridge (Next supplies no execution context on its own); handle() wires it to Next's after() for you.

When to use

Use @nextrush/adapter-nextjs if:

  • ✓ You want to mount a NextRush API inside a Next.js App Router project (app/api/.../route.ts)
  • ✓ You're on Next.js 14, 15, or 16

Reach for something else if:

  • ✗ You're on the Pages Router → migrate the route to app/api/[[...route]]/route.ts (the App Router has been Next's default for three major versions; this package deliberately does not support Pages — see ARCHITECTURE.md)
  • ✗ You're deploying to Cloudflare Workers/Vercel Edge/AWS Lambda directly (no Next.js involved) → use @nextrush/adapter-edge or @nextrush/adapter-serverless

Installation

pnpm add @nextrush/adapter-nextjs
# npm i @nextrush/adapter-nextjs · yarn add @nextrush/adapter-nextjs · bun add @nextrush/adapter-nextjs

[!NOTE] Already using nextrush? Import from nextrush/nextjs instead — same package, re-exported as an optional peer. A functional-only pnpm add nextrush never resolves it or next.

Quick start

Build the app in its own file, then mount it. Even a tiny API is two files, not one — the route file's only job is the bridge:

// src/server/app.ts
import { createApp, createRouter } from 'nextrush';

const app = createApp();

const api = createRouter();
api.get('/hello', (ctx) => ctx.json({ message: 'Hello Next.js!' }));
app.route('/api', api);

export { app };
// app/api/[[...route]]/route.ts
import { app } from '@/server/app';
import { handle } from 'nextrush/nextjs';

export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = handle(app);

No runtime export (Node is already the default; the Edge runtime is deprecated upstream), no mount-prefix configuration. The route is mounted the same way any NextRush route is mounted anywhere else: app.route('/api', router). See Project structure below for how this splits further as the API grows.

Capabilities

Capabilities

  • Seven-method exporthandle(app) returns { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } in one call
  • Class/DI support — pass a factory (() => Application | Promise<Application>) for apps needing await registerModule(...) before use
  • Background workctx.waitUntil() is wired to Next's after() when available, and never throws even if after() itself does
  • Mount-mismatch diagnostic — in development, a mounted-prefix mistake logs an actionable hint instead of a bare 404

Developer experience

  • Fully typed, zero any
  • No configuration for the common path — timeout and onError are the only options, both pass-throughs to the underlying engine

Mental model

The request is never touched. handle() forwards it, unmodified, to the same execution engine every edge/serverless target already uses:

Next.js route handler ──▶ handle() ──▶ createFetchHandler (unchanged) ──▶ your Application
                              │
                              └─ wires ctx.waitUntil() to after(), if available

Rule: mount prefixes are the application's own (app.route(prefix, router)) — this package never infers or strips one.

[!TIP] The full request lifecycle (Mermaid) is in ARCHITECTURE.md.


Common tasks

Project structure for a real API

app.route(prefix, router) composes the same way regardless of file count — split by feature, keep business logic out of route handlers, mount everything once in src/server/app.ts:

src/
├── server/
│   ├── app.ts                 # composes every feature router — the ONLY export route.ts imports
│   ├── routes/
│   │   ├── users.route.ts     # HTTP concerns only: params, status, calling into services
│   │   └── posts.route.ts
│   └── services/
│       ├── users.service.ts   # business logic, data access — no ctx, no Request/Response
│       └── posts.service.ts
app/
└── api/
    └── [[...route]]/
        └── route.ts           # the bridge — nothing else lives here
// src/server/services/users.service.ts — pure business logic, no framework types
export async function findUser(id: string) {
  return db.user.findUnique({ where: { id } }); // whatever your data layer is
}
// src/server/routes/users.route.ts — HTTP concerns only, delegates to the service
import { createRouter } from 'nextrush';
import { findUser } from '../services/users.service';

export const usersRouter = createRouter();

usersRouter.get('/:id', async (ctx) => {
  const user = await findUser(ctx.params.id);
  if (!user) return ctx.throw(404, 'User not found');
  ctx.json(user);
});
// src/server/app.ts — the one place every router gets mounted
import { createApp } from 'nextrush';
import { usersRouter } from './routes/users.route';
import { postsRouter } from './routes/posts.route';

const app = createApp();
app.route('/api/users', usersRouter);
app.route('/api/posts', postsRouter);

export { app };
// app/api/[[...route]]/route.ts — unchanged no matter how large the app grows
import { app } from '@/server/app';
import { handle } from 'nextrush/nextjs';

export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = handle(app);

This is the same shape Hono popularized: routers are plain, composable modules; the route file is a one-line bridge; and the app never knows or cares that it's running inside Next.js — the identical src/server/app.ts also boots under listen(), @nextrush/adapter-edge, or any other adapter, unchanged.

Mounting under a prefix

const app = createApp();
const api = createRouter();
api.get('/users', (ctx) => ctx.json(listUsers()));
app.route('/api', api); // requests to /api/users match

Class-based apps (DI, modules, guards)

import { registerModule } from 'nextrush/class';
import { AppModule } from '@/server/app.module';

export const { GET, POST } = handle(async () => {
  const app = createApp();
  await registerModule(app, AppModule, { prefix: '/api' });
  return app;
});

Building the app once across route-file reloads (dev HMR)

const g = globalThis as unknown as { __nextrushApp?: Application };
const app = (g.__nextrushApp ??= buildApp());

API overview

| Export | Signature | Since | Stability | Description | | ------ | --------- | ----- | --------- | ----------- | | handle | (app: AppSource, options?: NextHandlerOptions) => NextRouteHandlers | 1.0.0-beta.0 | Beta 🚧 | Mounts an app, returns all seven route-handler exports | | type AppSource | — | 1.0.0-beta.0 | Beta 🚧 | An Application, or a (possibly async) factory producing one | | type NextRouteHandlers | — | 1.0.0-beta.0 | Beta 🚧 | The seven exported handler functions | | type NextHandlerOptions | — | 1.0.0-beta.0 | Beta 🚧 | { timeout?, onError? } | | type NextRouteContext | — | 1.0.0-beta.0 | Beta 🚧 | The structural shape of Next's second handler argument |

Options

| Option | Type | Required | Default | Security-sensitive | Description | | ------ | ---- | -------- | ------- | ------------------ | ----------- | | timeout | number | No | the edge engine's default (25 000 ms) | — | Per-request timeout in ms, raced to a 504 | | onError | (error, ctx) => Response \| Promise<Response> | No | the engine's default 500 handler | — | Custom error → Response mapping |

Compatibility

Requirements

| Requirement | Version | | ----------- | ------- | | NextRush | 4.x | | Next.js | >=14.0.0 (App Router only) | | Node.js | >=22 | | TypeScript | >=5.x |

Runtimes

| Runtime | Supported | Notes | | ------- | --------- | ----- | | Node.js >=22 | ✅ | Next's default runtime | | Bun / Deno / Cloudflare (OpenNext) | ✅ / ✅ / ✅ | The package imports no runtime-specific API — pinned by packages/adapters/conformance's nextjs driver |

Integration

  • Peer dependencies: next >=14.0.0 (optional)
  • Works with: any NextRush middleware/class-runtime package — the mounted app is an ordinary Application
  • Incompatible with: the Pages Router (not supported — see ARCHITECTURE.md)

[!IMPORTANT] NextRush is ESM-only, permanently — no CommonJS build. On Node >=22, CJS consumers can require() this ESM package natively. See the Module Format Policy.


Troubleshooting

Cause: the mount prefix in your route file's folder doesn't match your app.route() call. Fix: in development, check the server log — a mismatch logs an actionable hint naming both halves and the exact app.route() call to add.

// app/api/[[...route]]/route.ts mounts at /api — the app must declare it too:
app.route('/api', api); // not app.route('/', api)

Cause: Next 14 statically caches GET route handlers by default (this changed to dynamic-by-default in 15.0.0-RC). Fix: add one export.

export const dynamic = 'force-dynamic';

Cause: that's exactly the problem this package exists to solve — createFetchHandler's second parameter (EdgeExecutionContext) isn't assignable from Next's route context. Fix: use handle(app) instead of createFetchHandler directly.

FAQ

Can I use this without nextrush? Yes — install @nextrush/adapter-nextjs directly and pass any @nextrush/core Application.

Why ESM-only? See the Module Format Policy.

Does it work on Bun / Deno / Edge? Yes — the package is fully Web-standard (no node:*, process, or runtime global), so it runs on every host Next.js itself runs on. Verified by the conformance suite's nextjs driver.

Does it support the Pages Router? No, deliberately — see ARCHITECTURE.md for why.


Package relationships

                        depends on            @nextrush/adapter-edge
@nextrush/adapter-nextjs ──────────────▶
                        often used with        @nextrush/class (for DI/module apps)
                        usually used next       @nextrush/openapi (for generated API docs)

Architecture

Maintaining or contributing to this package? The internal design — why the request is never rewritten, the mount-mismatch diagnostic, and the Pages Router non-goal — is in ARCHITECTURE.md. Design history: RFC-024, ADR-0014.

Resources


MIT © Tanzim Hossain