From NestJS
Map NestJS decorators and dependency injection to nextrush/class — every import below is verified against packages/class/src/index.ts.
NestJS and NextRush's class runtime (nextrush/class) both use decorators and constructor
injection, so the shape of a controller looks similar. The decorator names differ, and
NextRush's DI container is a focused wrapper around tsyringe
rather than Nest's own Angular-style container — smaller surface, fewer concepts.
Every import below is verified
Every decorator and function shown as importable from nextrush/class in this guide is
checked directly against that package's real export list
(packages/class/src/index.ts) — not assumed from NestJS's naming conventions. See
Deprecations for the full source-matched export list.
Decorator mapping
NestJS → nextrush/class
| Property | Type | Description |
|---|---|---|
@Injectable() | @Service() | Marks a class as a DI-managed service. Singleton by default in both frameworks. |
@Controller(path) | @Controller(path) | Same name, same purpose — groups routes under a path prefix. |
@Module({ ... }) | @Module({ ... }) | Same name. NextRush's fields are controllers, providers, imports, exports — see Modules. |
@Get() / @Post() / etc. | @Get() / @Post() / @Put() / @Patch() / @Delete() / @Head() / @Options() / @All() | Same route-method decorators; NextRush additionally has @All() for catch-all registration. |
@Body() / @Param() / @Query() | @Body() / @Param() / @Query() | Same names, same purpose — extract parts of the request into handler parameters. |
@Req() / @Res() | @Req() / @Res() | Same names. NextRush additionally exposes @Ctx() for the unified context object. |
@UseGuards(...) | @UseGuard(...) | Singular in NextRush. A guard is a GuardFn or a class implementing CanActivate, and returns a boolean rather than throwing. |
@UseInterceptors(...) | @UseInterceptor(...) | Singular in NextRush. |
@Catch(...) + implements ExceptionFilter | @Catch(...) + implements ExceptionFilter | Same shape — a class with a catch(error, ctx) method, attached with @UseFilter(...) (NextRush) vs @UseFilters(...) (Nest). |
OnModuleInit / OnModuleDestroy | OnInit / OnShutdown | NextRush's lifecycle hooks are duck-typed interfaces, not decorators — implement the interface, no decorator needed. |
Before / After
import { Injectable, Controller, Get, Module } from '@nestjs/common';
@Injectable()
class UserService {
findAll() {
return [{ id: 1, name: 'Alice' }];
}
}
@Controller('users')
class UserController {
constructor(private userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
}
@Module({
controllers: [UserController],
providers: [UserService],
})
class AppModule {}import { Service, Controller, Get, Module } from 'nextrush/class';
@Service()
class UserService {
findAll() {
return [{ id: 1, name: 'Alice' }];
}
}
@Controller('/users')
class UserController {
constructor(private userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
}
@Module({
controllers: [UserController],
providers: [UserService],
})
class AppModule {}Service, Controller, Get, and Module are all real exports of nextrush/class — confirmed
directly against packages/class/src/index.ts: export { Controller } from './decorators/class.js',
export { Module, ... } from './modules/module.js', export { Get, ... } from './decorators/routes.js',
and export { ..., Service, ... } from '@nextrush/di' (re-exported, not defined in class
itself).
Wiring the module into an app
NestJS bootstraps via NestFactory.create(AppModule). NextRush wires a module with
registerModule, which is also exported from nextrush/class:
import { createApp, listen } from 'nextrush';
import { registerModule } from 'nextrush/class';
import { AppModule } from './app.module';
const app = createApp();
await registerModule(app, AppModule);
await listen(app, 8080);For a single controller without a module, registerControllers (also in nextrush/class) does
filesystem discovery or takes an explicit list — see
Controllers reference.
Dependency injection — what's smaller
NestJS's DI container supports a broad Angular-derived feature set (custom providers, dynamic
modules, circular-reference forwarding, request-scoped providers via REQUEST). NextRush's
container (@nextrush/di, re-exported by nextrush/class) is intentionally narrower:
DI surface, re-exported from nextrush/class
| Property | Type | Description |
|---|---|---|
Service | decorator | Marks a class as DI-managed. Singleton by default; pass { scope } for transient or request scope. |
Repository | decorator | Identical behavior to @Service() — names a data-access class by role, not by scope. |
container | Container instance | The default global container — container.resolve(SomeService). |
createContainer | function | Creates an isolated container, mainly for tests. |
inject | function | Low-level resolve — same job as container.resolve, exposed as a standalone function. |
Container | type | The container interface type. |
Only these six DI symbols exist on nextrush/class
nextrush/class re-exports exactly Service, Repository, container, createContainer,
inject, and the Container type from @nextrush/di — confirmed by reading
packages/class/src/index.ts's final export block. There is no @Injectable, @Config,
@Optional, delay, or DIError-family export on nextrush/class — those exist only on
@nextrush/di directly. If you need them, import { ... } from '@nextrush/di', not
'nextrush/class'. This is exactly the class of mistake this rebuild caught and fixed in an
earlier phase — see Deprecations for the full picture.
There's no NextRush equivalent to NestJS's REQUEST-scoped injection token — request scope
in NextRush is a @Service({ scope: 'request' }) option, resolved from a per-request child
container automatically. See
Dependency Injection & Scopes for how scopes actually
work.
Guards return booleans, not decisions objects
NestJS guards implement canActivate() returning boolean | Promise<boolean> | Observable<boolean>.
NextRush guards are the same idea with a narrower return type — a GuardFn or a class
implementing CanActivate, returning boolean | Promise<boolean>:
import { UseGuard, Controller, Get } from 'nextrush/class';
import type { CanActivate, GuardContext } from 'nextrush/class';
class AuthGuard implements CanActivate {
canActivate(ctx: GuardContext): boolean {
return Boolean(ctx.get('authorization'));
}
}
@UseGuard(AuthGuard)
@Controller('/admin')
class AdminController {
@Get('/users')
getUsers() {
/* guard already ran */
}
}A guard returning false rejects the request with GuardRejectionError (403) — there's no
Observable variant to reason about. See Guards for the full model.
Exception filters
Both frameworks use a @Catch(...) decorator and a class implementing a catch method.
NextRush's attach decorator is @UseFilter (singular), matched against thrown errors via
instanceof in method-then-class precedence order:
import { Catch, Service } from 'nextrush/class';
import type { ExceptionFilter } from 'nextrush/class';
import type { Context } from '@nextrush/types';
import { NotFoundError } from 'nextrush';
@Service()
@Catch(NotFoundError)
class NotFoundFilter implements ExceptionFilter {
catch(error: unknown, ctx: Context): void {
ctx.status = 404;
ctx.json({ error: 'Resource not found' });
}
}See Exception Filters for the full matching model.
Next steps
- Modules — composing controllers and providers across features.
- Dependency Injection & Scopes — singleton, transient, and request scope.
- Guards, Interceptors, Exception Filters.
@nextrush/classreference — the complete export surface, grouped by concern.
From Fastify
Fastify's JSON Schema validation vs NextRush's bring-your-own-schema approach, and what else differs.
Deprecations
@nextrush/decorators and @nextrush/controllers were removed. Everything lives in nextrush/class now — here is the exact old-import → new-import map and the automated migration path.