Updated on 16 Dec, 202513 mins read 17 views

What Is a Service in NestJS?

In NestJS, a service is essentially a class annotated with the @Injectable() decorator that:

  • Encapsulates business logic
  • Is injectable via Nest's Dependency Injection (DI) system
  • Acts as a reusable provider
  • Separates concerns from controllers

Put simply – services do the “work” while controllers handle the “API”.

Service = business logic container

In NestJS:

  • Controllers: handle HTTP / transport concerns
  • Services: contain business rules, workflows, and orchestration
  • Repositories / data sources: persistence logic

Golden rule:

Controllers should be thin.

Services should be rich.

If you ever see:

@Controller()
export class UserController {
  @Post()
  createUser() {
    // DB logic here ❌
  }
}

That's a design smell.

Why NestJS Services Exist (Architectural Reason)

NestJS follows Dependency Injection (DI) + Inversion of Control (IOC)

Without services (bad design)

@Controller()
export class OrderController {
  private paymentService = new PaymentService();
}

Problems:

  • Tight coupling
  • Hard to test
  • Hard to replace implementation
  • No lifecycle management

With services (NestJS way)

@Injectable()
export class OrderService {
  constructor(private paymentService: PaymentService) {}
}

NestJS:

  • Creates instances
  • Manages lifetime
  • Injects dependencies
  • Enable mocking & testing

Why Services Matter

NestJS promotes a layered architecture:

Controller → Service → Repository / External APIs

Here's what services give you:

Separation of Concerns

Your controllers stay focused on HTTP routes and request handling. Your services focus on data processing, calculations, and business rules.

Testability

Services can be easily unit tested without depending of framework specific.

Reusability

One service can be used in many controllers or even in other services.

Maintainability

Clean layer separation makes your codebase easier to read and update.

Anatomy of a Service

Creating a Simple Service

Nest CLI makes it easy:

nest g service users

This generates:

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

@Injectable()
export class UsersService {
  private users = [];

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find(user => user.id === id);
  }
}

Here:

  • @Injectable() tells Nest this class can be injected as a provider.
  • It holds state (users) and methods (findAll, findOne).

Dependency Injection (DI) with Services

The real power of Nest services comes from DI. You can inject services into controllers or even other services.

Inject into a Controller

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

@Injectable()
export class UsersService {
  private users = [];

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find(user => user.id === id);
  }
}

Here, UsersService is injected via the constructor – clean, testable, and decoupling.

What @Injectable() Does

When you decorate a class with @Injectable(), NestJS:

  1. Registers the class as a provider
  2. Adds it to the IoC (Inversion of Control) container
  3. Allows it to be resolved and injected
@Injectable()
export class PaymentsService {}

Without this decorator, Nest cannot manage the class lifecycle.

Service Lifecycle & Scopes

Default: Singleton Scope

@Injectable()
export class UsersService {}
  • One instance per application
  • Shared across all requests
  • Best performance
  • Most common and recommended

Request Scope

@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {}
  • New instance per HTTP request
  • Useful for:
    • Request tracing
    • Per-request caching
  • Expensive – avoid unless necessary

Transient Scope

@Injectable({ scope: Scope.TRANSIENT })
  • New instance every injection
  • Rare use cases (e.g., stateful helpers)

Overusing non-singleton services can kill performance

 

Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *