NextRush
Getting Started

Framework Overview

NextRush at a glance — architecture, concepts, packages, and how requests move through the stack.

Framework overview

NextRush is a small, composable HTTP stack: one Context per request, Koa-style middleware, a fast router, optional controllers + DI, and adapters for Node.js, Bun, Deno, and Edge. Core packages stay dependency-free; you add @nextrush/* middleware and plugins only when you need them.

How to read this section

NextRush v3 (current line: 3.0.x) — semver patch releases; public APIs follow semver for breaking changes. Requires Node.js 22+. For positioning vs other stacks and a short mental model, read Introduction first; this page is the full stack map.

BenchmarksReproducibleLab setup — your numbers will differ
Core size< 3kLines of code
Packages30Monorepo
Runtimes4Node, Bun, Deno, Edge

Pick a path

New project

Start on the Installation page. Use pnpm create nextrush (or other forms) to run the create-nextrush package, or follow the manual install path. Installation · create-nextrush

Learn the model

Read Context, middleware, and routing first — everything else builds on that pipeline. Request lifecycle · Context · Middleware

Measure & tune

Reproducible benchmarks live in the docs; tuning guidance covers middleware order and serialization. Benchmarks · Tuning

Prerequisites

Node.js

22 or newer (LTS recommended)

TypeScript

Strict mode — matches the framework source

Package manager

pnpm, npm, yarn, or bun (some examples use pnpm)

For editor and tooling (lint, format), see Installation.

What this page covers

This is the broadest getting-started doc: runnable hello world, feature highlights, request lifecycle, concept links, package diagram, both programming styles, performance and runtime tables, and migration hints. Use the list below to jump to a section.

Errors, testing, and deployment

Production APIs need clear failures and checks — not only routing:

Hello World in under a minute

$ pnpm add nextrush
src/index.ts
import { createApp, createRouter, listen } from 'nextrush';

const app = createApp();
const router = createRouter();

router.get('/', (ctx) => ctx.json({ message: 'Hello NextRush' }));

app.route('/', router);
await listen(app, 3000);
// → http://localhost:3000

Why NextRush

Zero core dependencies

No external runtime packages in core, router, errors, types, adapters, or middleware packages. DI uses reflect-metadata (pulled in by the nextrush meta-package).

Multi-runtime

Same application code on Node.js, Bun, Deno, and Edge — swap the adapter, keep your routes and middleware.

Type-safe

Strict TypeScript throughout. No `any` in the public API. Inference from route params to handlers.

Dual paradigm

Functional routes for small services. Class-based controllers with DI when structure matters.

High throughput

Segment trie routing, pre-compiled middleware chain, minimal allocation on the hot path.

Modular

30 packages — cors, helmet, body-parser, controllers, websocket, and more as separate installs.

How a request flows

Every HTTP request follows the same path:

Loading diagram...
  1. Adapter receives the raw request and builds a Context
  2. Application runs the middleware pipeline
  3. Router matches the URL and extracts params
  4. Handler runs your logic and sets the response
  5. Response unwinds through middleware
  6. Adapter sends bytes to the client

Core concepts

Eight ideas cover most of the framework. Each has a dedicated concept page.

Application

The container for middleware, plugins, routes, and errors.

import { createApp } from 'nextrush';

const app = createApp();

Application →

Context

One object per request for input, output, and shared state.

router.get('/users/:id', (ctx) => {
  const { id } = ctx.params;
  const { page } = ctx.query;
  const auth = ctx.get('authorization');

  ctx.status = 200;
  ctx.json({ id, page, auth });
});

Context →

Middleware

Onion-style pipeline — code before ctx.next() runs inbound; code after runs outbound.

app.use(async (ctx) => {
  const start = Date.now();
  await ctx.next();
  ctx.set('X-Response-Time', `${Date.now() - start}ms`);
});
Loading diagram...

Middleware →

Routing

Segment trie with O(d) depth lookup; static routes use fast paths. Params, wildcards, and nested routers supported.

import { createRouter } from 'nextrush';

const users = createRouter();

users.get('/', (ctx) => ctx.json([]));
users.get('/:id', (ctx) => ctx.json({ id: ctx.params.id }));
users.post('/', (ctx) => ctx.json(ctx.body, 201));

app.route('/users', users);

Routing →

Plugins

Extend the app at startup: register middleware, routes, or lifecycle hooks without forking core.

import { controllersPlugin } from '@nextrush/controllers';
import { eventsPlugin } from '@nextrush/events';

app.plugin(eventsPlugin());
app.plugin(controllersPlugin({ router, root: './src' }));

Plugins →

Guards

Route-level access checks. Return false → 403 before the handler runs.

import type { GuardFn } from '@nextrush/decorators';

const AuthGuard: GuardFn = (ctx) => {
  return Boolean(ctx.get('authorization'));
};

@UseGuard(AuthGuard)
@Controller('/admin')
class AdminController {
  @Get('/dashboard')
  dashboard() {
    return { admin: true };
  }
}

Guards →

Dependency injection

Register services (singleton or transient) and inject into controllers.

import { Service } from '@nextrush/di';

@Service()
class UserService {
  async findAll() {
    return [{ id: 1, name: 'Alice' }];
  }
}

@Controller('/users')
class UserController {
  constructor(private users: UserService) {}

  @Get()
  findAll() {
    return this.users.findAll();
  }
}

Adapters

Runtime-specific HTTP I/O. Your app code stays the same.

import { listen } from 'nextrush'; // Node.js (default)
// import { listen } from '@nextrush/adapter-bun';
// import { listen } from '@nextrush/adapter-deno';
// import { listen } from '@nextrush/adapter-edge';

await listen(app, 3000);

Package architecture

30 packages in the monorepo. Lower layers never import from higher layers:

Loading diagram...

Package hierarchy →

Two programming styles

Use functions for small services; add controllers and DI when the API grows. Both styles can coexist.

Direct routes and middleware.

src/index.ts
import { createApp, createRouter, listen } from 'nextrush';
import { cors } from '@nextrush/cors';
import { json } from '@nextrush/body-parser';

const app = createApp();
const router = createRouter();

app.use(cors());
app.use(json());

router.get('/users', (ctx) => {
  ctx.json([{ id: 1, name: 'Alice' }]);
});

router.post('/users', (ctx) => {
  ctx.json(ctx.body, 201);
});

router.get('/users/:id', (ctx) => {
  ctx.json({ id: ctx.params.id });
});

app.route('/api', router);
await listen(app, 3000);

Controllers with DI.

src/index.ts
import { createApp, createRouter, listen } from 'nextrush';
import { controllersPlugin, Controller, Get, Post, Body, Service } from 'nextrush/class';

@Service()
class UserService {
  private users = [{ id: 1, name: 'Alice' }];

  findAll() {
    return this.users;
  }

  create(data: { name: string }) {
    const user = { id: this.users.length + 1, ...data };
    this.users.push(user);
    return user;
  }
}

@Controller('/users')
class UserController {
  constructor(private userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.findAll();
  }

  @Post()
  create(@Body() data: { name: string }) {
    return this.userService.create(data);
  }
}

const app = createApp();
const router = createRouter();

await app.plugin(controllersPlugin({ router, root: './src', prefix: '/api' }));
app.route('/', router);

await listen(app, 3000);

The nextrush meta-package loads reflect-metadata for decorators.

Mix and match

Use functional routes for health checks and class controllers for domain APIs in the same app — no forced migration path.

Design constraints

AreaConstraint
ThroughputNo universal RPS figure — use Benchmarks on your CPU, Node version, and tooling
Core lines of code< 3,000
Runtime dependencies0 in core; reflect-metadata for DI via the meta-package

Segment trie routing, pre-compiled middleware composition, and avoiding extra allocations on the request path keep overhead low. Cold-start and idle memory depend on process and OS — measure in your environment if those drive your product.

Full benchmarks →

Runtime support

RuntimeAdapterStatus
Node.js 22+nextrush (built-in)Stable
Bun@nextrush/adapter-bunStable
Deno@nextrush/adapter-denoStable
Edge (Cloudflare Workers, Vercel)@nextrush/adapter-edgeStable

The nextrush package ships the Node adapter. Install other adapters when you target Bun, Deno, or Edge.

Coming from another stack?

TopicExpressFastifyNextRush
Request surfacereq / res / nextrequest / replySingle ctx + ctx.next() (Koa-style)
RoutingLinear match orderRadix tree (find-my-way)Segment trie (@nextrush/router)
TypesDefinitelyTypedFirst-classStrict, first-party types
ValidationManual / middlewareJSON Schema (built-in)Bring Zod, Valibot, etc. (transforms on params)
StructureFree-formEncapsulated pluginsOptional controllers + DI via nextrush/class

Express

  • Middleware: (req, res, next) vs one ctx and ctx.next()
  • Routing: linear scan vs segment trie (O(depth))
  • Types: @types/express vs first-party strict types

Fastify

  • Plugins: Fastify encapsulation vs flat NextRush plugin() installs
  • Validation: JSON Schema vs transforms (Zod, Valibot, …)
  • Decorators: add-on only vs @Controller / @Get with DI when you want them

What ships in nextrush

PackageRole
@nextrush/coreApplication, middleware engine
@nextrush/routerSegment trie router
@nextrush/adapter-nodeNode HTTP server
@nextrush/errorsHTTP errors (4xx / 5xx)
@nextrush/typesShared types

Optional layers:

CategoryExamples
Middlewarecors, helmet, body-parser, compression, cookies, rate-limit, request-id, timer
Pluginscontrollers, events, logger, static, template, websocket
Adaptersadapter-bun, adapter-deno, adapter-edge
DIdi, decorators

Next steps

Community

On this page