In NestJS, a provider is any class that can be injected as a dependency, and a service is the most common kind — a class that holds business logic. Marking a class @Injectable() makes it a provider that NestJS's DI system can create and inject.
In NestJS, a provider is any class that can be injected as a dependency, and a service is the most common kind — a class that holds business logic. Marking a class @Injectable() makes it a provider that NestJS's DI system can create and inject.
import { Injectable } from "@nestjs/common";
@Injectable() // marks the class as a provider (injectable)
export class UsersService {
private users = [];
findAll() { return this.users; } // business logic
findOne(id: string) { return this.users.find(u => u.id === id); }
create(data) { this.users.push(data); return data; }
}
The @Injectable() decorator tells NestJS this class participates in dependency injection — it can be injected into controllers or other providers.
@Controller("users")
export class UsersController {
// NestJS sees this parameter and INJECTS a UsersService instance automatically
constructor(private usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll(); // use the injected service
}
}
You declare the dependency as a constructor parameter; NestJS creates and supplies the instance — you never write new UsersService().
@Module({
controllers: [UsersController],
providers: [UsersService], // register the provider so DI can resolve it
exports: [UsersService], // make it available to OTHER modules that import this one
})
export class UsersModule {}
Providers must be registered in a module's providers array for DI to know about them, and exports shares them with other modules.
By default, NestJS creates ONE shared instance of each provider (singleton scope)
for the whole application → efficient, shared state.
(Other scopes exist: REQUEST-scoped, TRANSIENT — but singleton is the default.)
Controller → handles HTTP (routing, request/response)
Service → handles business logic (rules, data access, computations)
→ Separation makes logic REUSABLE (multiple controllers can use one service),
TESTABLE (test the service in isolation), and ORGANIZED.
Providers and services are core to NestJS's architecture and its dependency-injection-driven design.
Services hold the application's business logic, kept separate from controllers (which handle only HTTP concerns) — this separation of concerns makes logic reusable across controllers, easily testable in isolation, and well-organized.
Understanding that @Injectable() marks a class as an injectable provider, that providers are registered in modules and injected via constructors (DI creating the instances for you), and that they're singletons by default is fundamental everyday NestJS knowledge.
The provider/service pattern, combined with controllers and modules, is the backbone of every NestJS application — mastering it is essential for building well-structured, maintainable back-ends and is among the first concepts to learn in the framework.