6月4日 15:43

What is the role of NestJS providers and services?

Provider Concept

Providers are a core concept in NestJS. They are classes decorated with @Injectable() that can be injected into other classes. Providers can handle business logic, data access, integration with other services, etc.

Provider Types

  1. Services: Encapsulate business logic
  2. Repositories: Handle data access
  3. Factories: Create and configure objects
  4. Providers: Any class that can be injected

Services

Services are the most common provider type, used to encapsulate business logic and reusable functionality.

Basic Service Structure

typescript
import { Injectable } from '@nestjs/common'; @Injectable() export class UsersService { private readonly users: any[] = []; create(user: any) { this.users.push(user); return user; } findAll() { return this.users; } findOne(id: number) { return this.users.find(user => user.id === id); } update(id: number, updateUserDto: any) { const user = this.findOne(id); if (user) { Object.assign(user, updateUserDto); return user; } return null; } remove(id: number) { const index = this.users.findIndex(user => user.id === id); if (index > -1) { this.users.splice(index, 1); return true; } return false; } }

Using Services in Controllers

typescript
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'; import { UsersService } from './users.service'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() create(@Body() createUserDto: any) { return this.usersService.create(createUserDto); } @Get() findAll() { return this.usersService.findAll(); } }

Registering Providers in Modules

Method 1: Direct Class Provision

typescript
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [UsersController], providers: [UsersService], }) export class UsersModule {}

Method 2: Using provide and useClass

typescript
@Module({ providers: [ { provide: UsersService, useClass: UsersService, }, ], }) export class UsersModule {}

Method 3: Using provide and useValue

typescript
@Module({ providers: [ { provide: 'API_KEY', useValue: 'your-api-key-here', }, ], }) export class AppModule {}

Method 4: Using provide and useFactory

typescript
@Module({ providers: [ { provide: UsersService, useFactory: (userRepository: UserRepository) => { return new UsersService(userRepository); }, inject: [UserRepository], }, ], }) export class UsersModule {}

Method 5: Using provide and useExisting

typescript
@Module({ providers: [ UsersService, { provide: 'USERS_SERVICE', useExisting: UsersService, }, ], }) export class UsersModule {}

Dependency Injection Tokens

Class as Token

typescript
constructor(private readonly usersService: UsersService) {}

String as Token

typescript
constructor(@Inject('API_KEY') private readonly apiKey: string) {}

Symbol as Token

typescript
export const API_KEY = Symbol('API_KEY'); constructor(@Inject(API_KEY) private readonly apiKey: string) {}

Scope Configuration

Default Scope (Singleton)

typescript
@Injectable() export class UsersService {}

Request Scope

typescript
import { Scope } from '@nestjs/common'; @Injectable({ scope: Scope.REQUEST }) export class UsersService {}

Transient Scope

typescript
@Injectable({ scope: Scope.TRANSIENT }) export class UsersService {}

Custom Providers

Async Providers

typescript
@Module({ providers: [ { provide: 'ASYNC_CONNECTION', useFactory: async () => { const connection = await createConnection(); return connection; }, }, ], }) export class AppModule {}

Dynamic Modules

typescript
import { DynamicModule, Module } from '@nestjs/common'; @Module({}) export class DatabaseModule { static register(options: DatabaseOptions): DynamicModule { return { module: DatabaseModule, providers: [ { provide: 'DATABASE_OPTIONS', useValue: options, }, DatabaseService, ], exports: [DatabaseService], }; } }

Best Practices

  1. Single Responsibility Principle: Each service should only be responsible for one functional domain
  2. Dependency Injection: Use constructor injection for dependencies
  3. Interface Segregation: Define clear interface contracts
  4. Avoid Circular Dependencies: Avoid circular dependencies between services during design
  5. Use DTOs: Use Data Transfer Objects to pass data
  6. Error Handling: Handle business logic errors in the service layer
  7. Testability: Write testable service code
  8. Documentation: Add clear documentation comments to services

Service Layer Design Patterns

1. Repository Pattern

typescript
@Injectable() export class UserRepository { constructor(private readonly dataSource: DataSource) {} async findById(id: number) { return this.dataSource.getRepository(User).findOne({ where: { id } }); } async findAll() { return this.dataSource.getRepository(User).find(); } }

2. Service Pattern

typescript
@Injectable() export class UsersService { constructor( private readonly userRepository: UserRepository, private readonly emailService: EmailService, ) {} async createUser(createUserDto: CreateUserDto) { const user = await this.userRepository.create(createUserDto); await this.emailService.sendWelcomeEmail(user.email); return user; } }

3. Factory Pattern

typescript
@Injectable() export class UserFactory { createUser(data: any): User { return new User(data); } }

Summary

The NestJS provider and service system provides:

  • Flexible dependency injection mechanism
  • Multiple provider registration methods
  • Clear code organization structure
  • High testability
  • Good maintainability

Mastering providers and services is the core of building NestJS applications, enabling developers to write loosely coupled, reusable, and testable code. By properly using providers and services, you can build well-structured, maintainable enterprise applications.

标签:NestJS