Skip to content

Keyed Services ​

Keyed services allow you to register multiple implementations of the same interface with different keys.

Basic Usage ​

typescript
interface ICache {
  get<T>(key: string): T | null;
  set<T>(key: string, value: T): void;
}

class BigCache implements ICache {
  // Large cache implementation
}

class SmallCache implements ICache {
  // Small cache implementation
}

// Register with keys
const ICacheToken = Symbol('ICache');
services.addKeyedSingleton<ICache>(ICacheToken, BigCache, 'big');
services.addKeyedSingleton<ICache>(ICacheToken, SmallCache, 'small');

Retrieving Keyed Services ​

Use getKeyedService() to retrieve by key:

typescript
const bigCache = await provider.getKeyedService<ICache>(ICacheToken, 'big');
const smallCache = await provider.getKeyedService<ICache>(ICacheToken, 'small');

String and Symbol Keys ​

You can use both string and symbol keys:

typescript
// String key
services.addKeyedSingleton<ICache>(ICacheToken, BigCache, 'big');

// Symbol key
const smallKey = Symbol('small');
services.addKeyedSingleton<ICache>(ICacheToken, SmallCache, smallKey);

// Retrieve
const big = await provider.getKeyedService<ICache>(ICacheToken, 'big');
const small = await provider.getKeyedService<ICache>(ICacheToken, smallKey);

Using in Services ​

Use factories to inject keyed services:

typescript
class UserService {
  constructor(private cache: ICache) {}

  async getUser(id: number) {
    const cached = this.cache.get(`user:${id}`);
    if (cached) return cached;
    // ... fetch from database
  }
}

const userServiceFactory = async (provider: ServiceProvider) => {
  const cache = await provider.getKeyedService<ICache>(ICacheToken, 'big');
  if (!cache) {
    throw new Error('Big cache not found');
  }
  return new UserService(cache);
};

services.addScoped<IUserService>(IUserServiceToken, userServiceFactory);

Use Cases ​

  • Different cache strategies - Big cache, small cache, distributed cache
  • Storage backends - Memory storage, disk storage, cloud storage
  • Database connections - Read replica, write database
  • API clients - Production API, staging API, mock API

Keyed vs Multiple Implementations ​

Keyed Services ​

  • Explicit key-based lookup
  • Good when you need to choose specific implementation
  • Use getKeyedService(token, key)

Multiple Implementations ​

  • Get all implementations
  • Good when you need all implementations
  • Use getServices(token)

Best Practices ​

  1. Use descriptive keys - 'big', 'small', 'production', etc.
  2. Use constants for keys - Avoid magic strings
  3. Check for null - getKeyedService() returns undefined if not found
  4. Use factories - For injecting keyed services into other services

Next Steps ​

Released under the ISC License. If you find this project helpful, consider buying me a coffee ☕