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 APIsHere'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 usersThis 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:
- Registers the class as a provider
- Adds it to the IoC (Inversion of Control) container
- 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
Leave a comment
Your email address will not be published. Required fields are marked *


