Doug.Instance

Code Reuse with Nest

Jun 27, 2023 | Code nestnpmreuse

I am currently working on a new application build which is using Nest. Honestly, I haven't worked with Nest before - mostly because I don't write much code in my roles these days and I don't usually lean towards large, "all-inclusive, " frameworks. Generally, I feel like these frameworks cause people to fall into anti-patterns. However, I like the approach of Nest and it does actually seem to guide adopters into a pretty good structure. A prime example popped up when we needed to reuse our CRUD code in a queue listener. At first, this seemed like a daunting task if you try to figure out how to instantiate all of the various services and dependencies manually. Thankfully, the Nest Standalone Applications documentation includes a very clear and simple approach to programmatically instantiate anything associated with your AppModule. I implemented this in a static method on the AppModule class as follows:

export class AppModule {
  public static async createServiceInstance<TInput = any, TResult = TInput>(typeOrToken: Type<TInput> | string | symbol): Promise<TResult> {
    const app = await NestFactory.createApplicationContext(AppModule);
    return app.get(typeOrToken);
  }
}

This allows you to create an instance of any service using something like AppModule.createServiceInstance(ServiceType).

Even though most people who are used to building Nest apps are probably used to building unit tests in Nest, you obviously need to be able to instantiate mock services even if you are reusing Nest code. To do this, just follow the patterns in the Nest testing documentation - specifically Test.createTestingModule - to create an instance of a test module that can then be used to instantiate your services and other dependencies.

Finally, you need to be able to actually import your Nest project into your other code base to use it. Since our queue listener required changes to the Nest app, we added the Nest app as a submodule so we could work on changes to both code bases together. Then we imported the Nest app as a module into the listener code base. Down the road, we will publish the Nest app to a private registry as an NPM package, but for now, this approach works just fine. There are plenty of posts on publishing a TypeScript package, but I'll summarize here for the important bits when it comes to importing the package from the local file system.

Changes to package.json for the Nest app:

  • Assign a logical value for name since this is what you will be importing
  • Set private to true since we are not publishing to a registry
  • Set main to the transpiled JavaScript file of an entry point to expose our types (in our case dist/src/index.js which was generated from src/index.ts)
  • Set types to the generated type definition file matching main (in our case dist/src/index.d.ts)

Changes to tsconfig.json for the Nest app:

  • module should be set to commonjs
  • declaration should be set to true
  • baseUrl should be set to the root of any absolute paths used in your local imports (in our case ./ since we root our imports at ./src)
  • files should be set to your transpiled output (in our case ["dist/**/*"]

Finally, after you build the Nest app, import it into the project where you want to reuse it using the file system:

npm i -D ./path/to/nest/app

Or if you prefer to symlink your module to automatically account for changes:

npm link ./path/to/nest/app
npm link nest-app-module-name