title: "Providers · Nael Framework" description: "Learn about providers, dependency injection, and service management in Nael Framework."

Overview

Providers

Providers are a fundamental concept in Nael. Many of the basic classes may be treated as providers – services, repositories, factories, helpers, and so on. The main idea of a provider is that it can be injected as a dependency; this means objects can create various relationships with each other.

Services

Let's start by creating a simple UsersService. This service will be responsible for data storage and retrieval, and is designed to be used by the UsersController, so it's a good candidate to be defined as a provider.

users.service.ts
import { Injectable } from '@nl-framework/core';@Injectable()export class UsersService {  private readonly users: User[] = [];  create(user: User) {    this.users.push(user);  }  findAll(): User[] {    return this.users;  }  findOne(id: number): User {    return this.users.find(user => user.id === id);  }}

The @Injectable() decorator attaches metadata, which declares that UsersService is a class that can be managed by the Nael IoC container. This example also uses a User interface, which probably looks something like this:

interfaces/user.interface.ts
export interface User {  id: number;  name: string;  email: string;}

Dependency injection

Nael is built around the strong design pattern commonly known as Dependency injection. We recommend reading a great article about this concept in the official Angular documentation.

In Nael, thanks to TypeScript capabilities, it's extremely easy to manage dependencies because they are resolved just by type. In the example below, Nael will resolve the usersService by creating and returning an instance of UsersService (or, in the normal case of a singleton, returning the existing instance if it has already been requested elsewhere). This dependency is resolved and passed to your controller's constructor:

users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nl-framework/http';@Controller('users')export class UsersController {  constructor(private usersService: UsersService) {}  @Post()  async create(@Body() createUserDto: CreateUserDto) {    this.usersService.create(createUserDto);  }  @Get()  async findAll() {    return this.usersService.findAll();  }  @Get(':id')  async findOne(@Param('id') id: string) {    return this.usersService.findOne(+id);  }}

Scopes

Providers normally have a lifetime ("scope") synchronized with the application lifecycle. When the application is bootstrapped, every dependency must be resolved, and therefore every provider has to be instantiated. Similarly, when the application shuts down, each provider will be destroyed. However, there are ways to make your provider lifetime request-scoped as well.

Request-scoped provider
import { Injectable, Scope } from '@nl-framework/core';@Injectable({ scope: Scope.REQUEST })export class UsersService {}

Optional providers

Occasionally, you might have dependencies which do not necessarily have to be resolved. For instance, your class may depend on a configuration object, but if none is passed, the default values should be used. In such a case, the dependency becomes optional.

Optional dependency
import { Injectable, Optional, Inject } from '@nl-framework/core';@Injectable()export class HttpService {  constructor(@Optional() @Inject('HTTP_OPTIONS') private httpOptions) {}}

In the example above, we are using the @Optional() decorator, which marks a dependency as optional.

Property-based injection

The technique we've used so far is called constructor-based injection, as providers are injected via the constructor method. In some very specific cases, property-based injection might be useful. For instance, if your top-level class depends on either one or multiple providers, passing them all the way up by calling super() in sub-classes from the constructor can be very tedious.

Property injection
import { Injectable, Inject } from '@nl-framework/core';@Injectable()export class HttpService {  @Inject('HTTP_OPTIONS')  private readonly httpOptions;}

Provider registration

Now that we have defined a provider (UsersService), and we have a consumer of that service (UsersController), we need to register the service with Nael so that it can perform the injection. We do this by editing our module file and adding the service to the providers array of the @Module() decorator.

users.module.ts
import { Module } from '@nl-framework/core';@Module({  controllers: [UsersController],  providers: [UsersService],})export class UsersModule {}

Custom providers

Nael has a built-in inversion of control ("IoC") container that resolves relationships between providers. This feature underlies the dependency injection feature described above, but is in fact far more powerful than what we've described so far.

Value providers: useValue

The useValue syntax is useful for injecting a constant value, putting an external library into the Nael container, or replacing a real implementation with a mock object.

Custom value provider
const connectionProvider = {  provide: 'CONNECTION',  useValue: connection,};@Module({  providers: [connectionProvider],})export class AppModule {}

Class providers: useClass

The useClass syntax allows you to dynamically determine a class that a token should resolve to.

Dynamic class provider
const configServiceProvider = {  provide: ConfigService,  useClass: process.env.NODE_ENV === 'development'    ? DevelopmentConfigService    : ProductionConfigService,};@Module({  providers: [configServiceProvider],})export class AppModule {}

Factory providers: useFactory

The useFactory syntax allows for creating providers dynamically. The actual provider will be supplied by the value returned from a factory function.

Factory provider
const connectionFactory = {  provide: 'CONNECTION',  useFactory: (optionsProvider: OptionsProvider) => {    const options = optionsProvider.get();    return new DatabaseConnection(options);  },  inject: [OptionsProvider],};@Module({  providers: [connectionFactory],})export class AppModule {}

Asynchronous providers

At times, the application start should be delayed until one or more asynchronous tasks are completed. For example, you may not want to start accepting requests until the connection with the database has been established. You can achieve this using asynchronous providers.

Async provider
{  provide: 'ASYNC_CONNECTION',  useFactory: async () => {    const connection = await createConnection();    return connection;  },}

The application will wait for the async provider to resolve before bootstrapping. This pattern works with useFactory syntax.

Export providers

Any provider that is part of a module can be exported. They can be exported either by their token or by their full provider object.

Exporting providers
import { Module } from '@nl-framework/core';@Module({  providers: [UsersService],  exports: [UsersService],})export class UsersModule {}

What's next?

Continue to Modules to learn how to organize your application structure, or explore Middleware for request processing.