Skip to content

What it is

NestJS is a structured Node.js framework built on TypeScript decorators, with dependency injection, modules and a strong architectural convention.

Applications are composed of modules containing controllers and providers. Dependencies are injected by type, and cross-cutting concerns are handled by guards, interceptors, pipes and filters.

Installation

npm i -g @nestjs/cli && nest new project

Getting started

The smallest useful thing you can do with it, and what each part means.

Controller, service and module
@Injectable()
export class BooksService {
  constructor(private readonly repo: BookRepository) {}
  findOne(id: string) { return this.repo.findById(id); }
}

@Controller('books')
export class BooksController {
  constructor(private readonly books: BooksService) {}

  @Get(':id')
  async findOne(@Param('id') id: string) {
    const book = await this.books.findOne(id);
    if (!book) throw new NotFoundException(`Book ${id} not found`);
    return book;
  }
}

@Module({
  controllers: [BooksController],
  providers: [BooksService, BookRepository],
})
export class BooksModule {}
Dependencies are resolved by constructor type, so tests can substitute a fake repository without touching the controller. Built-in exceptions map to the right HTTP status automatically.
Validation with pipes and DTOs
export class CreateBookDto {
  @IsString() @MaxLength(200)
  title: string;

  @IsInt() @Min(1400)
  year: number;
}

// main.ts — apply globally
app.useGlobalPipes(new ValidationPipe({
  whitelist: true,           // strip unknown properties
  forbidNonWhitelisted: true, // or reject them outright
  transform: true,           // turn plain objects into DTO instances
}));
Without `whitelist`, a client can send extra properties that pass straight through to your service. This configuration is the safe default and is not on by default.

Advanced usage

Where the library earns its place over a simpler alternative.

Guards and interceptors
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const required = this.reflector.get<string[]>('roles', context.getHandler());
    if (!required) return true;
    const { user } = context.switchToHttp().getRequest();
    return required.some((role) => user?.roles?.includes(role));
  }
}

@Injectable()
export class TimingInterceptor implements NestInterceptor {
  intercept(ctx: ExecutionContext, next: CallHandler) {
    const started = Date.now();
    return next.handle().pipe(
      tap(() => Logger.log(`${ctx.getHandler().name} ${Date.now() - started}ms`)),
    );
  }
}
Guards decide whether a request proceeds; interceptors wrap the response stream. This separation is what keeps controllers free of auth and logging code.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Nest can't resolve dependencies of the X
The provider is not in the module's providers array, or its module does not export it. The error names the missing parameter index.
Validation decorators are ignored
ValidationPipe is not registered, or the DTO is an interface. Decorators need a real class — interfaces are erased.

Best practices

  • Enable ValidationPipe with whitelist and forbidNonWhitelisted globally, or unknown fields flow through.
  • Keep controllers thin — they should translate HTTP to a service call and back.
  • Use the built-in HTTP exceptions so status codes stay consistent.
  • Choose Nest when the team wants enforced structure; it is heavy for a small API.

Background

Why it exists, and what it was reacting to.

Created by Kamil Myśliwiec and modelled on Angular's architecture, NestJS brought Spring-style structure to Node — appealing to teams from Java or C# backgrounds who found Express too unopinionated for large applications.