Services are the most common type of provider in a NestJS application, holding the actual business logic that controllers delegate to. This lesson covers how to design and structure a clean service layer.
What Belongs in a Service?
A service encapsulates a single area of business logic, validating rules, coordinating multiple repositories, calling external APIs, so that controllers stay focused purely on HTTP concerns.
Services are ordinary @Injectable() classes with no special base class required, they can have any constructor, methods, and internal state your feature needs.
import { Injectable, NotFoundException } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats = [{ id: 1, name: 'Whiskers' }];
findOne(id: number) {
const cat = this.cats.find((c) => c.id === id);
if (!cat) {
throw new NotFoundException('Cat not found');
}
return cat;
}
}
The service, not the controller, decides what counts as "not found" and throws the matching exception. The controller simply calls findOne() and lets Nest's exception layer handle the response.
Services frequently depend on other services, an OrdersService might inject ProductsService to check stock, and PaymentsService to charge a customer, composing smaller units of logic into a larger workflow.
Because services receive their dependencies through the constructor rather than importing them directly, tests can pass mock objects in place of real dependencies without any special mocking framework tricks.
Common Mistakes
Letting one service grow to handle several unrelated responsibilities.
Injecting the Express Request object into a service, coupling it tightly to HTTP.
Duplicating validation logic across services instead of centralizing it in a DTO or pipe.
Returning raw database entities directly instead of mapping them to response DTOs.
Key Takeaways
Services hold business logic, keeping controllers thin and focused on HTTP concerns.
Services are plain @Injectable() classes with no required base class.
Services commonly inject repositories and other services to compose behavior.
Constructor-based injection makes services straightforward to test with mocks.
Pro Tip
If a service constructor is accumulating five or more dependencies, treat that as a signal, it often means the service is doing too much and should be split along clearer responsibility lines.
You now understand how to design a NestJS service layer. Next, dig deeper into Dependency Injection itself, the mechanism that wires all of this together.