ConceptsCore Framework

Core Concepts

The mental models that make NextRush predictable and composable

Before you write code, understand how NextRush thinks.

These are the core abstractions that determine how your application processes requests, composes behavior, and handles failure.

The Four Pillars

NextRush is built on four core abstractions. Everything else builds on top of these.

How They Work Together

When a request arrives, it flows through a predictable pipeline:

HTTP Request

┌─────────────────────────────┐
│  Application                │
│  ┌───────────────────────┐  │
│  │  Middleware Pipeline  │  │
│  │  ┌─────────────────┐  │  │
│  │  │  cors → auth    │  │  │
│  │  │      ↓          │  │  │
│  │  │  body-parser    │  │  │
│  │  │      ↓          │  │  │
│  │  │  router         │  │  │
│  │  └─────────────────┘  │  │
│  └───────────────────────┘  │
│           ↓                 │
│  ┌───────────────────────┐  │
│  │  Router               │  │
│  │  Match path → Handler │  │
│  └───────────────────────┘  │
└─────────────────────────────┘

HTTP Response

The flow is always the same:

  1. Request arrives at the adapter
  2. Adapter creates a fresh Context object
  3. Application passes context through Middleware
  4. Router matches the path and extracts params
  5. Handler executes and writes to context
  6. Response flows back through middleware
  7. Adapter sends the HTTP response

Context is Per-Request

Every request gets a new ctx object. There's no shared state between requests unless you explicitly create it.

Two Programming Styles

NextRush supports both functional and class-based programming. Choose based on your project's needs.

Simple, direct, minimal abstraction. Often fits small services and APIs.

Functional API
import { createApp, createRouter } from 'nextrush';

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

router.get('/users/:id', async (ctx) => {
  const user = await db.users.findById(ctx.params.id);
  ctx.json({ user });
});

app.route('/api', router);

Structured, with dependency injection. Often fits larger applications and shared codebases.

Class-Based with DI
import { createApp } from 'nextrush';
import { Controller, Get, Param, Service, registerControllers } from 'nextrush/class';

@Service()
class UserService {
  async findById(id: string) {
    return db.users.findById(id);
  }
}

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

  @Get('/:id')
  async getUser(@Param('id') id: string) {
    return { user: await this.users.findById(id) };
  }
}

const app = createApp();
await registerControllers(app, { root: './src' });

You Can Mix Styles

Both styles work together. Start with functions, add classes when you need structure. There's no forced migration.

Extension Points

Beyond the four pillars, NextRush provides three ways to extend functionality — unequal in weight. Reach for middleware first; it covers nearly everything.

Middleware (the default, ~99%)

Almost every capability — security, parsing, logging, docs — is added with app.use():

import { openapi } from '@nextrush/openapi';

app.use(openapi({ router: app.router!, info: { title: 'API', version: '1.0.0' } }));

Registrar (~0.9%)

A plain function you call directly to wire up a subsystem, like class-based controllers:

import { registerControllers } from 'nextrush/class';

await registerControllers(app, { root: './src' });

Extension (~0.1%, rare)

For long-lived, app-scoped services that need a boot phase and a teardown phase — reserved for framework-level infrastructure like the event bus:

import { events } from '@nextrush/events';

app.extend(events());
await app.ready();

Learn the full taxonomy →

Guards

Control access to routes declaratively:

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

Learn about Guards →

The Class Runtime's Deeper Subsystems

Once an application grows past a handful of controllers, nextrush/class provides four more subsystems for structuring larger codebases. Each builds on dependency injection, which itself is worth understanding first.

What You Should Learn First

Context API

Understand the ctx object—it's your primary interface for everything. Read Context →

Middleware

Learn the onion model. It determines execution order and error handling. Read Middleware →

Routing

Map URLs to handlers efficiently with segment trie routing. Read Routing →

Mental Model Summary

ConceptResponsibilityKey Insight
ApplicationOrchestrationHolds everything together, manages lifecycle
ContextRequest/ResponseFresh per-request, mutable state for sharing
MiddlewareTransformationOnion model—before, next(), after
RoutingURL MatchingO(k) segment trie, params and wildcards
ExtendingCapabilityMiddleware (~99%), Registrar (~0.9%), Extension (~0.1%)
GuardsAccess ControlBoolean gatekeepers for routes

The class runtime's deeper subsystems build on top of these:

ConceptResponsibilityKey Insight
DI & ScopesObject LifetimeSingleton / transient / request, with bubbling
ModulesCompositionGroup controllers + providers, compose via imports
InterceptorsResult ShapingOnion around a controller method's return value
Exception FiltersError ScopingLocalized catch blocks per controller/route
LifecycleBoot/ShutdownOnInit/OnShutdown, duck-typed, no decorator

Common Misconception

Middleware order matters. Error handlers must come first. Body parsers must run before you access ctx.body. If something isn't working, check your registration order.

Next Steps

Ready to structure a larger application? Continue into the class runtime's deeper subsystems:

Was this helpful?

On this page