mrkeyoor.com_
Wed 05 Aug 05:06 UTC
npmWeb Backendupdated 05 Aug 2026

@nestjs/core

NestJS is a full server-side framework for Node.js, built in TypeScript, that gives your backend an enforced architecture: modules, dependency injection, controllers with decorators, and a layered request pipeline of guards, pipes, interceptors, and exception filters. The design is openly inspired by Angular. Under the hood it runs Express by default (Express 5 as of Nest 11) or Fastify via an adapter, so the HTTP layer is swappable. @nestjs/core is the runtime container; you always pair it with @nestjs/common and a platform adapter.

Verdict

The strongest choice in Node for large, long-lived backends built by teams, because the structure it imposes is the product. For small services the same structure is mostly paperwork; reach for something lighter.

API stability4/5The core decorator and DI API has been stable across many majors; majors arrive roughly yearly with real but well-documented breaks (v11 moved to Express 5, changing wildcard routes and query parsing).
Docs5/5docs.nestjs.com covers fundamentals, techniques, recipes, and every official package with runnable samples; it is one of the most complete doc sites in the Node ecosystem.
Maintenance5/5Pushed to daily with frequent patch releases (11.1.28), only 42 open issues and PRs at 76k stars, corporate sponsorship, and a dedicated core team around its creator.
Ecosystem5/5First-party packages for GraphQL, microservices, WebSockets, CQRS, scheduling, and config, plus a large third-party module ecosystem and Fastify/Express interop.

Use it if

  • You have a team of more than a few backend developers and want every feature to follow the same module/controller/service shape without arguing about it
  • You are building a large API that will grow into GraphQL, WebSockets, microservices, or queues: Nest has official first-party packages for each behind one DI system
  • You come from Angular, Spring, or .NET and want constructor injection, decorators, and testable units instead of hand-wired Express middleware
  • You need structured testing: @nestjs/testing lets you swap any provider with a mock through the DI container instead of monkey-patching imports
Skip it if

Setup reality

The blessed path is npm i -g @nestjs/cli && nest new project, which scaffolds tsconfig, ESLint, Jest, and the app skeleton for you; doing it by hand means installing @nestjs/common, @nestjs/core, @nestjs/platform-express, rxjs, and reflect-metadata (all peer-coupled to the same major), then enabling experimentalDecorators and emitDecoratorMetadata in tsconfig. Validation needs two more packages (class-validator, class-transformer) that nothing installs for you. Expect npm peer-dependency warnings around optional packages like @nestjs/websockets and @nestjs/microservices even when you do not use them, and expect major-version upgrades (10 to 11 moved to Express 5 and path-to-regexp 8) to change wildcard route syntax.

Patterns

Bootstrap the application (main.ts)bootstrap-app

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  app.setGlobalPrefix('api');
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

import 'reflect-metadata' must run before anything else; the CLI scaffold handles it, hand-rolled setups forget it and DI fails cryptically.

Declare a feature moduledefine-module

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService], // visible to modules that import CatsModule
})
export class CatsModule {}

A provider is private to its module unless listed in exports; the 'Nest can't resolve dependencies' error is almost always a missing import/export.

Controller with route params and status codescontroller-route

import { Controller, Get, Post, Param, Body, HttpCode } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.catsService.findOne(id);
  }

  @Post()
  @HttpCode(201)
  create(@Body() dto: CreateCatDto) {
    return this.catsService.create(dto);
  }
}

Return a value and Nest serializes it to JSON; inject @Res() and you take over the response entirely, silently disabling interceptors for that handler.

Injectable service via constructor injectioninjectable-service

import { Injectable, NotFoundException } from '@nestjs/common';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  findOne(id: string): Cat {
    const cat = this.cats.find((c) => c.id === id);
    if (!cat) throw new NotFoundException(`Cat ${id} not found`);
    return cat;
  }
}

Built-in HttpException subclasses (NotFoundException etc.) become proper HTTP responses automatically; plain Errors become opaque 500s.

Validate request bodies with DTOsdto-validation

// npm i class-validator class-transformer
import { IsString, IsInt, Min } from 'class-validator';

export class CreateCatDto {
  @IsString() name: string;
  @IsInt() @Min(0) age: number;
}

// main.ts
app.useGlobalPipes(new ValidationPipe({
  whitelist: true,           // strip unknown properties
  transform: true,           // convert primitives to DTO types
}));

Without whitelist: true, unknown JSON fields pass straight through to your service layer; class-validator and class-transformer are separate installs.

Protect routes with a guardauth-guard

import { CanActivate, ExecutionContext, Injectable, UseGuards } from '@nestjs/common';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(ctx: ExecutionContext): boolean {
    const req = ctx.switchToHttp().getRequest();
    return req.headers['x-api-key'] === process.env.API_KEY;
  }
}

@UseGuards(ApiKeyGuard)
@Controller('admin')
export class AdminController {}

Guards run before pipes and interceptors; returning false yields a 403, so throw UnauthorizedException yourself if you want a 401.

Load environment config the Nest wayconfig-env

// npm i @nestjs/config
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot({ isGlobal: true })],
})
export class AppModule {}

// anywhere via DI:
constructor(private config: ConfigService) {}
this.config.get<string>('DATABASE_URL');

@nestjs/config wraps dotenv; isGlobal: true saves you importing ConfigModule into every feature module.

Shape error responses with an exception filterexception-filter

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpErrorFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const res = host.switchToHttp().getResponse();
    res.status(exception.getStatus()).json({
      statusCode: exception.getStatus(),
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

// main.ts: app.useGlobalFilters(new HttpErrorFilter());

Globally registered filters instantiated with new cannot use DI; register them as APP_FILTER providers if they need injected dependencies.

Swap Express for Fastifyfastify-adapter

// npm i @nestjs/platform-fastify
import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
);
await app.listen(3000, '0.0.0.0');

Fastify is faster but Express-specific middleware and libraries expecting req/res from Express will not work; listen needs the 0.0.0.0 host in containers.

Unit test with the DI containerunit-testing

import { Test } from '@nestjs/testing';

const moduleRef = await Test.createTestingModule({
  controllers: [CatsController],
  providers: [CatsService],
})
  .overrideProvider(CatsService)
  .useValue({ findOne: jest.fn().mockReturnValue({ id: '1' }) })
  .compile();

const controller = moduleRef.get(CatsController);
expect(controller.findOne('1')).toEqual({ id: '1' });

overrideProvider swaps real services for mocks without touching module code; for HTTP-level tests add supertest against moduleRef.createNestApplication().

Graceful shutdown hookslifecycle-shutdown

import { Injectable, OnModuleDestroy } from '@nestjs/common';

@Injectable()
export class DbService implements OnModuleDestroy {
  async onModuleDestroy() {
    await this.pool.end();
  }
}

// main.ts
app.enableShutdownHooks(); // required for SIGTERM handling

Shutdown hooks are off by default because they cost a signal listener; without enableShutdownHooks() your onModuleDestroy never fires on SIGTERM in Kubernetes.

Alternatives

PackageRegistryPick it when
fastifynpmYou want speed and schema-based validation without a framework dictating your architecture
hononpmSmall services or edge/serverless deploys where startup weight and runtime portability matter
expressnpmYou want the minimal, everyone-knows-it baseline and are happy to pick your own structure