NestJS Providers and Services
Providers are one of the most important concepts in NestJS. Almost everything that is not a controller or a module is a provider — services, repositories, factories, helpers. The most common type of provider is a service. This topic explains what providers are, how services work, and how NestJS manages them behind the scenes.
What Is a Provider?
A provider is any class that NestJS can create and inject into other classes automatically. The word "provider" describes the role: this class provides something — a feature, a piece of data, a utility — to other parts of the application.
Think of a power socket on a wall. The socket provides electricity. Anything that needs electricity — a lamp, a laptop, a fan — plugs into the socket. The lamp does not generate its own electricity. It receives it. In NestJS, a service is the socket. A controller is the lamp. The controller plugs in and receives what it needs.
What Is a Service?
A service is a provider whose job is to hold business logic. When a controller needs to find a user, validate a password, send an email, or process an order, it calls a service method. The service does the actual work.
A service is a plain TypeScript class decorated with @Injectable(). This decorator tells NestJS that this class can be injected into other classes.
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find(user => user.id === id);
}
create(name: string) {
const newUser = { id: this.users.length + 1, name };
this.users.push(newUser);
return newUser;
}
}
How a Service Connects to a Controller
Request: GET /users/1
|
v
UsersController
findOne(@Param('id') id)
|
| calls
v
UsersService
findOne(1) → { id: 1, name: 'Alice' }
|
v
Response: { "id": 1, "name": "Alice" }
The controller focuses on routing. The service focuses on logic. Neither crosses into the other's territory.
Registering a Service in a Module
For NestJS to inject a service, you register it in the module's providers array:
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
When the CLI generates a service with nest g service users, it registers the service in the module automatically. You do not need to add it manually.
The @Injectable Decorator
The @Injectable() decorator marks a class as a candidate for dependency injection. Without it, NestJS cannot inject the class into other components.
Every time you create a service manually (not via CLI), add @Injectable() at the top of the class. Forgetting this decorator causes a runtime error where NestJS cannot find the provider.
Types of Providers
Services are the most common providers, but NestJS supports other types too:
Provider Type | Purpose ----------------|---------------------------------------------- Service | Business logic (most common) Repository | Database access layer Factory | Dynamically creates other providers Value Provider | Injects a plain value or constant Class Provider | Swaps one class for another (useful in testing)
Value and Factory Providers
You can inject non-class values using the object syntax in the providers array:
// Injecting a constant value
providers: [
{
provide: 'API_KEY',
useValue: 'abc-123-xyz',
}
]
// Injecting it in a service
constructor(@Inject('API_KEY') private apiKey: string) {}
Factory providers let you run code to determine the provider value at runtime — useful when you need to set up a connection or read an environment variable before creating the provider.
Scope of a Provider
By default, NestJS creates one instance of each provider per module and reuses it for every request — this is the singleton scope. Two controllers in the same module share the exact same service instance.
For advanced use cases, you can change the scope:
Scope | Instance Created ----------------|--------------------------------------------- DEFAULT | Once per application (singleton) — default REQUEST | Once per incoming HTTP request TRANSIENT | A fresh instance every time it is injected
Singleton scope suits most applications. Request scope works well when a provider must hold request-specific data like the current logged-in user.
Exporting Providers
A provider registered in one module is private to that module by default. To share it with other modules, export it from the exports array:
@Module({
providers: [UsersService],
exports: [UsersService], ← makes it available to importing modules
})
export class UsersModule {}
Any module that imports UsersModule can then inject UsersService without re-registering it.
Services are the workhorses of a NestJS application. Every meaningful operation — reading from a database, processing a payment, sending a notification — belongs in a service. Keeping this logic out of controllers ensures each part of your code has one clear purpose and remains easy to test independently.
