JoonDong
JoonDong

Reputation: 151

is not Injectable decorator required in providers?

I'm studying NestJS with a sample app.

https://github.com/nestjs/nest/tree/master/sample/12-graphql-schema-first

However, what I am curious about is that even if the service does not have an injectable decorator, it can be registered as a provider of the module, and the constructor of other providers can use the registered provider without the injectable decorator.

Actually, I removed the injectable decorator from src/cats/cats.service.ts in the example above. But it works fine.

Even without the Injectable decorator, the same object was passed to the provider's constructor.

Why is the injectable decorator necessary?

Upvotes: 2

Views: 1314

Answers (2)

Jay McDoniel
Jay McDoniel

Reputation: 70490

The answer here is: "it depends".

If a provider has no injected dependencies, then technically, no you don't need the @Injectable() decorator. What that decorator is doing under the hood is forcing Typescript to emit constructor parameter metadata. Nest will read that metadata at runtime to know what is injected into the provider. This PR goes into more depth on the @Injectable() decorator itself

If we have the following classes

@Injectable()
export class Foo {
  constructor(private readonly foo: string) {}
}
@Injectable()
export class Bar {
  constructor(private readonly bar: string) {}
}
@Injectable()
export class FooBar {
  constructor(private readonly foo: Foo, private readonly bar: Bar) {}
}

Then we get a compiled output like this

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
let Foo = class Foo {
    constructor(foo) {
        this.foo = foo;
    }
};
Foo = __decorate([
    Injectable(),
    __metadata("design:paramtypes", [String])
], Foo);
export { Foo };
let Bar = class Bar {
    constructor(bar) {
        this.bar = bar;
    }
};
Bar = __decorate([
    Injectable(),
    __metadata("design:paramtypes", [String])
], Bar);
export { Bar };
let FooBar = class FooBar {
    constructor(foo, bar) {
        this.foo = foo;
        this.bar = bar;
    }
};
FooBar = __decorate([
    Injectable(),
    __metadata("design:paramtypes", [Foo, Bar])
], FooBar);
export { FooBar };

The __metadata('design:paramtypes', []) is what Nest eventually reads, and matches to the DI container injection tokens

Upvotes: 2

Micael Levi
Micael Levi

Reputation: 6695

injectable is to allow injecting providers in that class. If you don't have anything to inject, you don't need that decorator. But I'd suggest you to always use it.

For detailed info on this topic, read this: https://github.com/nestjs/docs.nestjs.com/pull/2481

Upvotes: 1

Related Questions