# Injection scopes

The scope of a Provider defines the lifecycle and visibility of that bean in the context in which it is used.

Ts.ED DI defines 3 types of : singleton, request and instance which can be used on Provider, Service, Middleware and Controller.

# Singleton scope

Singleton scope is the default behavior of all providers. That means all providers are created during server initialization.

import {Controller, Get, ProviderScope, Scope} from "@tsed/common";

@Controller("/")
@Scope(ProviderScope.SINGLETON)  // OPTIONAL, leaving this annotation a the same behavior
export class MyController {
  private rand = Math.random() * 100;

  @Get("/random")
  async getValue() {
    return this.rand;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

Note

In this example all requests on /random endpoint return the same random value.

# Request scope

Request scope will create a new instance of provider for each incoming request. A new container will be created and attached to the request. It will contain all providers annotated by @Scope(ProviderScope.REQUEST).

import {Controller, Get, ProviderScope, Scope} from "@tsed/common";

@Controller("/")
@Scope(ProviderScope.REQUEST)
export class MyController {
  private rand = Math.random() * 100;

  @Get("/random")
  async getValue() {
    return this.rand;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

Each request on /random will return a different random value.

# Chain with Service

It is also possible to use @Scope(ProviderScope.REQUEST) on a service if your service is injected on a controller which is annotated by @Scope(ProviderScope.REQUEST) too.

Here is a working example:

import {Controller, Get, ProviderScope, Scope, Service} from "@tsed/common";

@Service()
@Scope(ProviderScope.REQUEST)
export class MyService {
  public rand = Math.random() * 100;
}

@Controller("/")
@Scope(ProviderScope.REQUEST)
export class MyController {
  constructor(private myService: MyService) {
  }

  @Get("/random")
  async getValue() {
    return this.myService.rand;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

And here is an unworking example:

import {Controller, Get, ProviderScope, Scope, Service} from "@tsed/common";

@Service()
@Scope(ProviderScope.REQUEST)
export class MyService {
  public rand = Math.random() * 100;
}

@Controller("/")
@Scope(ProviderScope.SINGLETON) // SINGLETON avoid all Scope("request") annotation
export class MyController {
  constructor(private myService: MyService) {
  }

  @Get("/random")
  async getValue() {
    return this.myService.rand;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

WARNING

The SINGLETON annotation avoids the @Scope(ProviderScope.REQUEST) annotation put on MyService.

WARNING

The @Scope(ProviderScope.REQUEST) annotation has no effect on Global middlewares.

# Performance

Using request-scoped providers will obviously affect application performance. Even though Ts.ED is trying to cache as much metadata as possible, it will still have to create an instance of your class on each request. Hence, it will slow down your average response time and overall benchmarking result. If your provider doesn't necessarily need to be request-scoped, you should rather stick with the singleton scope.

# Instance scope

Instance scope used on a provider tells the injector to create a new instance each time the provider is injected to another one.

import {Controller, Get, ProviderScope, Scope, Service} from "@tsed/common";

@Service()
@Scope(ProviderScope.INSTANCE)
export class MyInstanceService {
  private rand = Math.random() * 100;

  @Get("/random")
  async getValue() {
    return this.rand;
  }
}

@Controller("/")
@Scope(ProviderScope.SINGLETON)
export class MyController {
  constructor(instance1: MyInstanceService, instance2: MyInstanceService) {
    console.log("IsSame", instance1 === instance2);
    console.log("instance1", instance1.getValue());
    console.log("instance2", instance2.getValue());
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22