Careeroza
Injection Tokens and @Inject
Tokens that are Not Classes
When the provider key is a string, a symbol or an interface, NestJS cannot guess it from the type. Use @Inject(TOKEN) on the constructor parameter.
src/products/products.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { GST_RATE } from '../payments/payments.module';
export interface ProductRepository {
findById(id: number): Promise<{ id: number; price: number } | null>;
}
export const PRODUCT_REPOSITORY = Symbol('PRODUCT_REPOSITORY');
@Injectable()
export class ProductsService {
constructor(
@Inject(PRODUCT_REPOSITORY) private readonly repo: ProductRepository, // interface + token
@Inject(GST_RATE) private readonly gstRate: number,
) {}
async priceWithGst(id: number) {
const product = await this.repo.findById(id);
return product ? Math.round(product.price * (1 + this.gstRate)) : null;
}
}Tip: Prefer Symbol('NAME') or exported constants over raw strings. A typo in a string token is only found when the app starts.
Programming to an Interface
Example
Tokens let you depend on an interface and choose the implementation in the module. Here the service works with either repository:
interface ProductRepository {
findById(id: number): { id: number; price: number } | null;
}
class InMemoryProductRepository implements ProductRepository {
private rows = [{ id: 7, price: 1299 }];
findById(id: number) { return this.rows.find((r) => r.id === id) ?? null; }
}
class CachedProductRepository implements ProductRepository {
private cache = new Map<number, { id: number; price: number } | null>();
constructor(private inner: ProductRepository) {}
findById(id: number) {
if (!this.cache.has(id)) {
console.log(' cache miss for', id);
this.cache.set(id, this.inner.findById(id));
}
return this.cache.get(id) ?? null;
}
}
class ProductsService {
constructor(private repo: ProductRepository, private gstRate: number) {}
priceWithGst(id: number) {
const p = this.repo.findById(id);
return p ? Math.round(p.price * (1 + this.gstRate)) : null;
}
}
const plain = new ProductsService(new InMemoryProductRepository(), 0.18);
console.log('plain:', plain.priceWithGst(7), plain.priceWithGst(99));
const cached = new ProductsService(new CachedProductRepository(new InMemoryProductRepository()), 0.18);
console.log('cached:', cached.priceWithGst(7));
console.log('cached:', cached.priceWithGst(7));Output
plain: 1533 null cache miss for 7 cached: 1533 cached: 1533