Angular services as tree-shakable providers

Published June 29, 2021

When creating services to be used with Angular's Dependency Injection system, a provider is needed for each service.

Providers in AppModule

By adding services as providers in the AppModule, the build tools and compilers are unable to know if that code is actually used in the application. They will always make these services available throughout the application even if they are never used. These services will not be able to take advantage of tree-shaking. More on tree-shaking down below.

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [],
  providers: [
    UserService,
    PostService,
    CategoryService, 
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Rather than adding all of your services into the providers array of AppModule, as seen above, Angular recommends setting the providedIn property of a service's @Injectable() decorator. This will make such services tree-shakable. The service will still need to be injected where it is used.

Root application injector

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService { } 

By using root, we specify that Angular should provide the UserService in the root injector. It will be available throughout the application. We've just created a singleton service, which is a service for which only one instance exists in an application.

When using providedIn on services, they will only be bundled in the application if they get used. These are called tree-shakable providers.

What is tree-shaking?

Tree shaking is a build process operation that helps with removing unused code from a code base. It's like shaking a a tree and having the dead leaves fall off the tree. Tree shaking only includes the code that is needed for the app to run.

Feature modules

We can also specify that a service should be provided in a particular @NgModule. If we don't want UserService to be available unless a UserModule is imported, then we can specify that the service should be provided only in the module.

import { Injectable } from '@angular/core';
import { UserModule } from './user.module';

@Injectable({
  providedIn: UserModule,
})
export class UserService { }

This is the recommended way to provide a service in a feature module. This method enables tree-shaking of the service if nothing ends up injecting it.