Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@catalist-nestjs/di

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@catalist-nestjs/di

NestJS Dependency Injection runtime helpers for Catalist Finance projects. Part of [Catalist NestJS Modules](https://github.com/blockarchivelabs/catalist-nestjs-modules/#readme)

  • 1.0.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Nest JS Dependency Injection runtime helpers

NestJS Dependency Injection runtime helpers for Catalist Finance projects. Part of Catalist NestJS Modules

Install

yarn add @catalist-nestjs/di

Motivation

This module exists to solve following things:

  1. the lack of runtime interfaces in Typescript
  2. necessity of @Inject decorators for constructor arguments when working with NestJS
  3. use of Symbol to point to specific implementation when working with NestJS

The problem

When working with NestJS in case you need two different implementations of one interface, you have to introduce multiple tokens (symbols), which point to different implementations.

// bar-service.interface.ts
export interface BarServiceInterface {
  someMethod(): string;
}
// bar-service-a.ts
export const BarServiceAToken = Symbol();

export class BarServiceA implements BarServiceInterface {
  someMethod(): string {
    return 'BarServiceA';
  }
}
// bar-service-b.ts
export const BarServiceBToken = Symbol();

export class BarServiceB implements BarServiceInterface {
  someMethod(): string {
    return 'BarServiceB';
  }
}
// foo-service.ts
import { Inject, Injectable } from '@nestjs/common';
import { BarServiceInterface } from './bar-service.interface';
import { BarServiceAToken } from './bar-service-a';
import { BarServiceBToken } from './bar-service-b';

@Injectable()
export class FooService {
  // it's necessary to inject `BarServiceAToken` symbol here to point to specific implementation
  public constructor(
    @Inject(BarServiceAToken) private barService: BarServiceInterface,
  ) {}

  public checkInstanceOf() {
    // this is impossible because Typescript types do not exist in runtime
    this.barService instanceof BarServiceInterface;
  }
}

Solution

Module exposes two primitives:

  1. createInterface<I>(nameOfInterface: string): InterfaceTag<I> function.

Creates special interface-like anonymous class InterfaceTag that acts like an interface utilizing Symbol.hasInstance method that allows to override behavior of instanceof operator.

  1. @ImplementsAtRuntime<T>(interfaceTag: InterfaceTag<T>): ClassDecorator class decorator

Class decorator indicating that class implements interface at runtime with the help of Reflect. Needed for proper work of instanceof operator for class instances.

Usage

Basic usage

import { createInterface, ImplementsAtRuntime } from '@catalist-nestjs/di';

interface FooInterface {
  foo(): string;
}

interface BarInterface {
  bar(): number;
}

const FooInterface = createInterface<FooInterface>('FooInterface');
const BarInterface = createInterface<BarInterface>('BarInterface');

@ImplementsAtRuntime(FooInterface)
export class FooBar implements FooInterface, BarInterface {
  bar(): number {
    return 2;
  }

  foo(): string {
    return 'bar';
  }
}

const foobar = new FooBar();

console.log(foobar instanceof FooInterface === true); // true
console.log(foobar instanceof BarInterface === true); // true

Nest.js usage

// service.interface.ts
import { createInterface } from '@catalist-nestjs/di';

export interface ServiceInterface {
  doSmth(): string;
}
// the interface name and the name of the constant should be the same
export const ServiceInterface =
  createInterface<ServiceInterface>('ServiceInterface');
// service.ts
import { ImplementsAtRuntime } from '@catalist-nestjs/di';
import { Injectable } from '@nestjs/common';

// here, we are importing the type and the constant in one variable 'ServiceInterface'
import { ServiceInterface } from './service.interface';

@Injectable()
@ImplementsAtRuntime(ServiceInterface)
export class ServiceA implements ServiceInterface {
  doSmth(): string {
    return 'serviceA';
  }
}

@Injectable()
@ImplementsAtRuntime(ServiceInterface)
export class ServiceB implements ServiceInterface {
  doSmth(): string {
    return 'serviceB';
  }
}
// service.module.ts
import { Injectable } from '@nestjs/common';
import { ServiceA, ServiceB } from './service';
import { ServiceInterface } from './service.interface';

export interface ServiceModuleOptions {
  service: 'A' | 'B';
}

@Module({})
export class ServiceModule {
  static forRoot(options?: ServiceModuleOptions): DynamicModule {
    return {
      global: true,
      ...this.forFeature(options),
    };
  }

  static forFeature(options?: ServiceModuleOptions): DynamicModule {
    return {
      module: ServiceModule,
      // depending on the options module can provide
      // different immplementation for `ServiceInterface`
      providers: [
        options?.service === 'A'
          ? {
              provide: ServiceInterface,
              useClass: ServiceA,
            }
          : {
              provide: ServiceInterface,
              useClass: ServiceB,
            },
      ],
      exports: [ServiceInterface],
    };
  }
}
// some-other-service.ts
export class SomeOtherService {
  // no `@Inject` decorator here
  public constructor(private service: ServiceInterface) {}

  public someMethod() {
    // implementation of doSmth depends on `ServiceModuleOptions` value
    this.service.doSmth();
  }
}

Keywords

FAQs

Package last updated on 30 Apr 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc