NestJS wonderful imports - Nest unable to resolve dependencies

Recently, I came across a tutorial at

Following the NestJS part of the tutorial, I encountered an error despite having code that seemed identical to the one in the tutorial.

[Nest] 94625  - 10/01/2024, 11:43:53 AM   ERROR [ExceptionHandler] Nest can't resolve dependencies of the TrpcRouter (?). Please make sure that the argument Function at index [0] is available in the TrpcModule context.

Potential solutions:
- Is TrpcModule a valid NestJS module?
- If Function is a provider, is it part of the current TrpcModule?
- If Function is exported from a separate @Module, is that module imported within TrpcModule?
  @Module({
    imports: [ /* the Module containing Function */ ]
  })

Below is the code snippet for that module:

import { Injectable, type INestApplication } from '@nestjs/common';
import type { TrpcService } from './trpc.service';
import { z } from 'zod';
import * as trpcExpress from '@trpc/server/adapters/express';

@Injectable()
export class TrpcRouter {
  constructor(private readonly trpc: TrpcService) {}

  appRouter = this.trpc.router({
    hello: this.trpc.procedure
      .input(z.object({ name: z.string().optional() }))
      .query(({ input }) => {
        return `Hello ${input.name ? input.name : 'World'}!`;
      }),
  });

  async applyMiddleware(app: INestApplication) {
    app.use(
      '/trpc',
      trpcExpress.createExpressMiddleware({ router: this.appRouter }),
    );
  }
}

export type AppRouter = TrpcRouter['appRouter'];

After various attempts, I found out that the root cause of the error was importing type rather than the whole module on this line:

import type { TrpcService } from './trpc.service';

By reverting it to the original tutorial version:

import { TrpcService } from './trpc.service';

the issue was resolved.

However, I am only using it as type and not as value:

  constructor(private readonly trpc: TrpcService) {}

This raises the question - why does this happen? Why does it throw an error when I am just importing the type and not the value? Could it be related to how typescript functions within NestJS?

PS: Although there are similar questions out there, none seem to address this specific difference between type import and regular import.

PPS: During my search regarding type imports, I stumbled upon this potential answer to my query. Currently exploring it further: https://github.com/nestjs/nest/issues/5421

Answer №1

If you still prefer to utilize the import type syntax for a specific reason, there is another method to define a provider for Nest's DI system. When defining a provider

providers: [
  {
    provider: "TrpcSvc", // use either a string or symbol here
    useClass: TrpcService
  }
]
...
import type { TrpcService } from './trpc.service';

// Inject
constructor(@Inject("TrpcSvc") private readonly trpc: TrpcService) {}

The crucial aspect is how you import the provider in the module. If you opt for default imports like provider: [TrpcService], this implies that you are adding the TrpcService class to the Nest's DI container with the key and value being the TrpcService class itself. It resembles this structure

import { TrpcService } ....
...
providers: [
  {
    provider: TrpcService,
    useClass: TrpcService

  }
]

Conversely, NestJS does not support the usage of type as the key of providers. This pertains to how NestJS defines module options.

...
/**
 * Interface defining the property object that describes the module.
 *
 * @see [Modules](https://docs.nestjs.com/modules)
 *
 * @publicApi
 */
export interface ModuleMetadata {
    ...
    /**
     * Optional list of providers that will be instantiated by the Nest injector
     * and that may be shared at least across this module.
     */
    providers?: Provider[];
    ...
}
...
// For class
export interface Type<T = any> extends Function {
    new (...args: any[]): T;
}
...
export type Provider<T = any> = Type<any> | ClassProvider<T> | ValueProvider<T> | FactoryProvider<T> | ExistingProvider<T>;

At present, providers only accept value and not type.

Answer №2

A key aspect of NestJS that developers should be aware of is how its dependency injection works. This factor is integral to the framework and cannot be easily fixed:

https://github.com/nestjs/nest/issues/5421#issuecomment-692295353

During the use of Node, not tsc, the dependency injection process occurs. When the build phase takes place, TypeScript code is transpiled into JavaScript. At this point, typed imports are validated for correct types, ultimately becoming {} akin to interfaces. It's important to understand that Nest cannot bypass this behavior; instead, it must be factored into the development process. Incorporating class interfaces, injection tokens, or custom providers can aid in managing this limitation. However, Nest's DI system cannot overcome the inherent actions of TypeScript with import type declarations.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Should Angular libraries be developed using Typescript or transpiled into JavaScript?

Currently, I am in the process of developing a library that will be available as an Angular module through npm. The library itself has been written using typescript. Everything was functioning perfectly up until Angular version 5.0.0, but after this update ...

Displaying dynamic key-value pairs in each row of an Angular mat-table

I need help displaying a key-value pair data in JSON format dynamically within a table using Angular mat-table. The keys will vary, so there is no set list of keys that will be included in the JSON. This is an example of the data: var data = { "cars" : 2 ...

What sets an Array apart from an Observable Array?

When working with TypeScript, how do any[] and Observable<any[])> differ from each other? What advantages and disadvantages does each one offer? ...

Error: Unable to use import statement outside of a module when deploying Firebase with TypeScript

Every time I try to run firebase deploy, an error pops up while parsing my function triggers. It specifically points to this line of code: import * as functions from 'firebase-functions'; at the beginning of my file. This is a new problem for me ...

Typescript and Apollo Client return types intertwined

My goal is to create a simple function within a class that generates an Apollo Client. Below is the code I have implemented: import appConfig from 'config/app-config'; import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/clie ...

Utilizing sourcemaps in ionic for seamless linking

I've encountered an issue with source maps or a similar feature. After inserting console.log(...) in my code, the message appears in the console but links to the compiled JavaScript file instead of the original TypeScript file. Have I overlooked som ...

What is the approach taken by this component to display its child elements?

While delving into the code of react-accessible-accordion, I found myself puzzled by the way it handles rendering its children. The snippet below is from Accordion.tsx: export default class Accordion extends React.Component<AccordionProps> { // ...

AngularTS - Using $apply stops the controller from initializing

Every time I launch the application, the angular {{ }} tags remain visible. Removing $scope.$apply eliminates the braces and displays the correct value. I am utilizing Angular with Typescript. Controller: module Application.Controllers { export class Te ...

Is there support for TypeScript in express-openid-connect?

Is there any documentation available for using express-openid-connect with TypeScript, or if it is supported at all? ...

What are the distinctions between type parameters in TypeScript?

With TypeScript, it is possible to pass different types of parameters like: public getClassesNumbers(classes: ClassesAndSubjects[] | SchoolClass[]) {} However, if the internal function expects only SchoolClass[] as the parameter type for classes, should ...

Is it possible to configure strict settings for specific files using tsconfig.json?

Check out my tsconfig.json file: { "compilerOptions": { "allowArbitraryExtensions":true, "target": "ES2021", "lib": [ "ES2021", "dom", "dom.iterable" ] ...

The functionality of the Auth0 Logout feature has ceased to work properly following the

Previously, the code worked flawlessly on Angular version 14. However, after updating to version 17 due to persistent dependency issues, a problem arose. logout(): void { sessionStorage.clear(); this.auth.logout({ returnTo: window.location.origin } ...

Data cannot be transferred to a child element unless it has been initialized during the definition phase

Passing an array data from parent to child component has brought up some interesting scenarios: parent.component.html: <child-component ... [options]="students" > </child-component> Status I: Setting the array on definition ...

Enhancing Application Performance Through Next.js Development

I'm currently working on an application using next.js and I am looking to implement code splitting in order to reduce the bundle size and load pages on demand. Unfortunately, I have not been able to find a way to do this without specifying routes. Fo ...

The constructor in a Typescript Angular controller fails to run

Once the following line of code is executed cockpit.controller('shell', shellCtrl); within my primary module, the shell controller gets registered with the Angular application's _invokeQueue. However, for some reason, the code inside t ...

The extensive magnetic scrolling functionality in Ionic 2 sets it apart from other frameworks

Hi everyone, I could really use some assistance! I've been working on developing an Ionic 2 App and my navigation setup is not too complex. I have a main menu where clicking on an item opens another menu with a submenu. From there, if I click on an i ...

Steps to fix the issue of 'Property 'foo' lacks an initializer and is not definitely assigned in the constructor' while utilizing the @Input decorator

What is the proper way to initialize a property with an @Input decorator without compromising strict typing? The code snippet below is triggering a warning: @Input() bar: FormGroup = new FormGroup(); ...

Leveraging import and export functionality in TypeScript while utilizing RequireJS as a dependency

I am in the process of transitioning a complex JavaScript application from Backbone/Marionette to TypeScript. While making this shift, I want to explore the benefits of exporting and importing classes using files as modules. Is it necessary to incorporat ...

What is the best way to configure the default entry point for a package.json file in a React

I'm having trouble with the default export in my package.json file. when I try to import: import { Component } from 'packagename/'; // size 22kb or import { Component } from 'packagename/dist' // size 22kb; but import { Component ...

Node.js is known for automatically adding double quotes to strings. However, is there a way to eliminate them?

After creating a unit test, I noticed that both logics I used led to the same issue. Logic 1: describe('aresFileCopier', () => { test('log error', async () => { await registerDB('ares-test', { client: ' ...