Overview
NextRush started as a from-scratch study of framework internals and grew into a real monorepo: 20+ packages under packages/ (core, router, di, decorators, runtime, types, errors, plus adapters for node/bun/deno/edge, 10 middleware packages, and 6 plugin packages), a Fumadocs-based documentation site, and a benchmark suite comparing it against Fastify, Express, Koa, and Hono.
The router is a segment trie — not a compressed radix tree, the two are often confused. Routes are indexed by path segment for O(d) lookup where d is segment depth; static routes get a separate hash map for O(1) lookup, and route executors are pre-compiled at registration time rather than per request. Dependency injection is an explicit, honest wrapper around tsyringe (Microsoft's DI library) rather than a from-scratch container — it adds circular-dependency detection and clearer error messages on top. Middleware follows the same Koa-style onion model as Express/Koa: (ctx, next) => Promise<void>, composed once at server-start time.
Benchmarking used autocannon across six frameworks (raw Node, Fastify, NextRush, Hono, Koa, Express) on identical hardware, with results committed to the repo as raw JSON, not just summarized in prose. The one place NextRush actually wins outright is the middleware-stack scenario — beating both Fastify and raw Node.js — which says more about pipeline design than about the router.
Two scoping notes worth being upfront about: the "under 3,000 LOC" and "zero dependencies" claims in the framework's own docs apply to the core/router/types/errors package group specifically, not the full 20+ package monorepo — the DI/decorators stack has two real runtime dependencies (tsyringe, reflect-metadata). And an internal architecture audit in the repo was self-conducted by an AI coding agent, not a third-party security review — it's a useful engineering artifact, but not independent validation.
/ Scope
- HTTP server core: middleware pipeline, plugin system, route mounting, error handling
- Segment-trie router with static-route hash-map fast path
- DI layer wrapping tsyringe, with circular-dependency detection
- Multi-runtime adapters: Node.js, Bun, Deno, Edge
- 10 middleware packages (CORS, Helmet, CSRF, rate-limit, compression, multipart, etc.)
- Docs site (Fumadocs/Next.js) and autocannon-based benchmark suite
Highlights
01
20+ package monorepo: core, router, DI, decorators, 10 middleware packages, 4 runtime adapters
02
Autocannon-benchmarked against Fastify/Express/Koa/Hono — 14% overhead vs raw Node.js, wins the middleware-stack scenario
03
Segment-trie router (not a compressed radix tree) with a static-route fast path
04
Fumadocs documentation site and a reproducible, raw-JSON benchmark suite
/ Tracks
- Research
- Open source
The Problem
Approach
Key Decisions
- 01
Not every layer is worth building from scratch
The router is a real segment trie built from scratch. DI is the opposite choice: rather than reinventing a container, it's an explicit wrapper around tsyringe with added circular-dependency detection. See Challenges for the router's actual mechanics — the point here is knowing which layer earns a from-scratch build and which doesn't. - 02
Koa-style middleware composition
Each middleware is (ctx, next) => Promise<void>, composed once at server-start into a single pipeline — not re-composed per request. Same pattern used by Koa and Express under the hood, but visible in about 30 lines instead of hidden in a dependency. - 03
Fixed-version core packages, independent everything else
10 core packages (types, errors, core, router, runtime, di, decorators, controllers, node adapter, meta package) are locked to one version via a Changesets 'fixed' group, so they can't drift apart. Middleware, plugins, and other adapters version independently. Documented as an RFC before implementing. - 04
Numbers you can check, not numbers you assert
Raw benchmark JSON is committed to the repo alongside the summary — see Challenges for the actual results. The point of this decision wasn't the specific numbers, it was making the numbers reproducible instead of just quotable.
Architecture
/ System proof
These are not tool badges. They describe the boundaries, consistency controls, async paths, and failure-mode decisions behind the build.
- 01Segment-trie router with static-route hash-map fast path — O(d) lookup
- 02Koa-style middleware pipeline, composed once at server-start
- 03DI layer wrapping tsyringe, with circular-dependency detection
- 04Multi-runtime adapters: Node.js, Bun, Deno, Edge
- 05Changesets fixed/independent versioning split across 20+ packages
Challenges & How I Solved Them
Async error propagation
/ Problem
/ Solution
Router design with params & wildcards
/ Problem
/ Solution
Framework overhead is easy to claim, hard to prove
/ Problem
/ Solution
Outcomes
- A working, benchmarked framework monorepo with 20+ packages, not just a core prototype
- Verified performance data (autocannon, raw JSON in-repo) rather than asserted numbers
- A Changesets-based fixed/independent versioning split that scales with a plugin-heavy package layout
What I Learned
- 01
Frameworks are a stack of small, individually boring decisions. None of them are clever on their own — the value is in how they compose.
- 02
Not every layer is worth reinventing — wrapping tsyringe for DI and building the router from scratch were both the right call for different reasons.
- 03
A benchmark you can't reproduce from committed raw data isn't a benchmark, it's a claim.
Tech Stack
Next Steps
- Request validation layer (schema-based, pluggable)
- Streaming response helpers
- Repeated-trial benchmark runs with statistical confidence, not single-run numbers