Providers and DI

Providers and Dependency Injection

In The Bookshelf API a service was a closure over a store: bookService(store). In Nest it is a class marked @Injectable(), and the container passes the store to its constructor. The rules inside are unchanged.

src/books/books.service.ts — the same rules, injected instead of closed overJavaScript
@Injectable()
export class BooksService {
  constructor(private readonly store: Store) {}
  async list(q: ListBooksDto) {
    const offset = (q.page - 1) * q.limit;              // the store never hears "page"
    const { total, items } = await this.store.listBooks({ ...q, offset });
    const pages = Math.max(1, Math.ceil(total / q.limit));
    return { data: items, page: { limit: q.limit, page: q.page, total, pages } }; }
  async get(id: string) {
    const book = await this.store.getBook(id);
    if (!book) throw new NotFoundException(`No book with id ${id}`);
    return { ...book, author: await this.store.getAuthor(book.authorId) };
  } }

addReview follows the same shape: await this.get(bookId) first so an unknown book is a 404, then throw new ConflictException(...) if store.findReview(caller.id, bookId) returns a row.

emitDecoratorMetadata makes TypeScript emit a design:paramtypes array beside each decorated class — the constructor's parameter classes, preserved into JavaScript. At boot Nest reads it with Reflect.getMetadata, looks up each class as a token in the module's provider registry, constructs dependencies first, then the class, and caches the instance. Default scope is singleton, which is why injection fails at boot and why the error names the class and the argument index.

Class tokens only work for classes. For a config value, an interface or a third-party object, write the token out: { provide: T, useClass: C } swaps an implementation, { provide: T, useValue: v } supplies a stub, and { provide: T, useFactory: fn, inject: [...] } covers async setup. useClass behind a token is how the store interface of Data Layer survives into MongoDB: the Mongoose 243,355 implementation registers under the same token, and no service changes.