Can you please provide an explanation on the functioning of Dependency Injection in Nestjs?

I have been delving into Nest.js and incorporating it into my project structure. Additionally, I have integrated TypeORM into the mix. The concept of Dependency Injection in Nest.js has me feeling a bit perplexed.

Project Structure

|-APP_MODULE
  |-app.controller.ts
  |-app.module.ts
  |-app.service.ts
  |-AUTH_MODULE
    |-SIGN_UP_DTO
      |-signup.dto.ts
    |-auth.controller.ts
    |-auth.module.ts
    |-password-hash.service.ts
  |-USER_MODULE
    |-user.controller.ts
    |-user.module.ts
    |-user.service.ts

app.module.ts

@Module({
  imports: [
    UserModule,
    ConfigModule.forRoot({
      validationSchema: envValidationSchema,
      validationOptions: {
        allowUnknown: true,
        abortEarly: true,
      },
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'sqlite',
        database: configService.getOrThrow('DATABASE_NAME'),
        entities: [],
        synchronize: configService.getOrThrow('DATABASE_SYNC_TABLES'),
        autoLoadEntities: true,
      }),
    }),
    PostModule,
    CommonModule,
    AuthModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

user.service.ts

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  getAllUsers(): Promise<UserEntity[]> {
    return this.userRepository.find();
  }

  getOneUserById(id: number) {
    return this.userRepository.findOneBy({ id: id });
  }
  getOneUserByEmail(email: string) {
    return this.userRepository.findOneBy({ email: email });
  }

  createUser(newUser: Partial<UserEntity>): Promise<UserEntity> {
    return this.userRepository.save(newUser);
  }
}

user.entity.ts

@Entity('users')
@Unique('user_email_unique', ['email'])
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  email: string;

  @Exclude()
  @Column()
  password: string;

  @Column({ default: false })
  isAdmin: boolean;

  @Exclude()
  @Column()
  salt: string;

  @OneToMany((type) => PostEntity, (post) => post.user)
  posts: PostEntity[];

  @CreateDateColumn()
  created_at: Date;

  @UpdateDateColumn()
  updated_at: Date;

  constructor(partial: Partial<UserEntity>) {
    Object.assign(this, partial);
  }
}

  1. Scenario - 1 (Not Working)

The export of user.service.ts from user.module.ts and its addition to the providers array in auth.module.ts results in an error message:

Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserEntityRepository at index [0] is available in the AuthModule context.

user.module.ts

@Module({
  imports: [TypeOrmModule.forFeature([UserEntity])],
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

auth.module.ts

@Module({
  imports: [],
  providers: [PasswordHashService, UserService], // -> Injecting UserService is giving error
  exports: [],
  controllers: [AuthController],
})
export class AuthModule {}

  1. Scenario 2 (working)

The export of password-hash.service.ts from auth.module.ts and its addition to the providers array in user.module.ts works seamlessly without any error.

user.module.ts

@Module({
  imports: [TypeOrmModule.forFeature([UserEntity])],
  controllers: [UserController],
  providers: [UserService, PasswordHashService], // -> Injecting PasswordHashService(no error)
  exports: [UserService],
})
export class UserModule {}

auth.module.ts

@Module({
  imports: [],
  providers: [PasswordHashService],
  exports: [PasswordHashService],
  controllers: [AuthController],
})
export class AuthModule {}

I require some clarification on the workings of DI in Nest.js.

Answer №1

Although this may not be a detailed explanation of Dependency Injection, I encountered the same issue while using scenario 1...

To resolve it, add your user.entity to the auth.module within TypeOrmForFeature().

Even when attempting to export your service, the service may be unable to access the required entity (user.entity).

This is why it is necessary to import the user.entity into your AuthModule when trying to utilize a service from a different module that cannot access the user.entity.

Simply include TypeOrmModule.forFeature([UserEntity]) in your AuthModule.

@Module({
  imports: [TypeOrmModule.forFeature([UserEntity])],
  providers: [PasswordHashService, UserService], // -> Injecting UserService is giving error
  exports: [],
  controllers: [AuthController],
})
export class AuthModule {}

Answer №2

To start off, imagine a module as a container for dependencies, like a mysterious black box wrapped up neatly. But here's the catch,

Why is the initial scenario falling short? Essentially, when you specify that AuthModule relies on UsersService, you must also define the dependencies of UsersService (which is why scenario 2 succeeds). What exactly are the dependencies of UsersService? They consist of:

imports: [TypeOrmModule.forFeature([UserEntity])]
. How can this be rectified? Simply import UsersModule in your auth.module.ts like so:

auth.module.ts

@Module({
  imports: [UsersModule], // This inclusion grants access to UsersService within AuthModule
  providers: [PasswordHashService],
  exports: [],
  controllers: [AuthController],
})
export class AuthModule {}

This will enable the utilization of UsersService within PasswordHashService or AuthController. Why opt for importing UsersModule rather than just UsersService? Consider a scenario where you have multiple providers with numerous dependencies within UsersModule; by importing the module, you essentially obtain all essential components necessary for running the module without having to individually import each dependency.

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

Adding the same block of code to an event in Node.js: Best practices

My preferred tech stack for real-time user synchronization includes Node.Js with Express and Express HBS (Handlebars), as well as Socket.IO. For example, when creating a web chat application, I emit an event from the client to the server each time a user ...

After constructing my Shopify app, my next step is to guide users to my website by utilizing email and shop URL parameters upon app installation

const Koa = require('koa'); const cors = require('@koa/cors'); var https = require('https'); var http = require('http'); const { default: enforceHttps } = require('koa-sslify'); const next = require('n ...

Issue with TypeScript: Declaring type for objects in an array using .map

I'm having trouble assigning types to the Item object that is being returned from unitedStates.map((item: Item) => {}. Despite my efforts, I am unable to correctly define the types. Although I have specified the unitedStates array of objects as un ...

Having trouble with the express installation, receiving an error stating "Package.json file not found."

I seem to be encountering an issue while trying to install express using the npm command npm install express npm WARN enoent ENOENT: no such file or directory, open 'C:\Program Files\Express\nodekb\package.json' > npm WAR ...

Caution: The attribute name `data-*` is not recognized as valid

I am attempting to import an SVG file in my NEXT.js project using the babel-plugin-inline-react-svg. I have followed all the instructions and everything is functioning perfectly. // .babelrc { "presets": ["next/babel"], "plugin ...

Establishing a path for a post request in a Node.js application

While setting up a basic registration page, I encountered an error when trying to establish a route for the post request of the user credentials. node:_http_outgoing:648 throw new ERR_HTTP_HEADERS_SENT('set'); ^ Error [ERR_HTTP_HEADERS_ ...

What is the best method to find a matching property in one array from another?

I am working with two arrays in TypeScript. The first one is a products array containing objects with product names and IDs, like this: const products = [ { product: 'prod_aaa', name: 'Starter' }, { product: 'prod_bbb&apos ...

What is the best method for ensuring image orientation is displayed correctly?

When utilizing multer node and express for uploading images to my application, I've noticed that some of the images appear rotated 90 degrees once they reach the client side. What could be causing this issue, and how can I resolve it? Just to clarif ...

Discovering a specific string within an array of nested objects

Seeking guidance on creating a dynamic menu of teams using JavaScript/TypeScript and unsure about the approach to take. Here is an example dataset: const data = [ { 'name': 'Alex A', 'agentId': '1225& ...

I'm having trouble locating my route for some unknown reason

I've set up a basic code structure to test my routes, but I keep getting a 404 error. Although I have an app.all function to catch errors, I'm having trouble pinpointing where the issue lies. Below is my index.js file, where I've configured ...

Using the get method instead of delete in an express application

As I was diving into the process of removing a collection from MongoDB using express, I came across the concept of app.delete for deletion. However, upon closer inspection of my code, it seems like the actual deletion is carried out by Todos or MongoDB thr ...

Is it possible to terminate an active server process triggered by an HTTP post request in Node.js prior to returning a response?

I developed an application where I utilized Ajax to make calls to a Node server. The issue is that even if the user navigates to another page, the server persists in processing the initial request made by the Ajax call. It then proceeds to process the new ...

Upon a successful AJAX post request, the page fails to display

I'm encountering an issue connecting my front-end JavaScript to my back-end Node/Express. Although the requests from my client-side js to the server return successful, the page I am requesting fails to render. On the server side, here is my code: ap ...

The Node.js Express Server runs perfectly on my own machine, but encounters issues when deployed to another server

I've encountered a strange issue. My node.js server runs perfectly fine on my local machine, but when I SSH into a digital ocean server and try to run it there, I get this error. I used flightplan to transfer the files to the new machine. deploy@myse ...

Tips on avoiding the conversion of the ✳ symbol into an emoji

My issue lies in my ✳ (Eight-Spoked Asterisk) symbol being converted to an emoji on iOS/android devices. Find more about the Eight-Spoked Asterisk Emoji here. Could someone guide me on how to prevent the normal symbol ✳ from being transformed into an ...

Attention: WARNING regarding the NEXTAUTH_URL in the Development Console

While working on my Next.js web application with next-auth for authentication, I came across a warning message in the development console. The message is related to reloading the environment from the .env.local file and compiling certain modules within the ...

defaultValue cannot be used with createContext

Currently, I am endeavoring to establish a context utilizing the createContext method from the react API: import React, { useState, createContext } from 'react'; export const MovieContext = createContext(); export const MovieProvider = (props) ...

Is it possible to utilize the System.import or import() function for any JavaScript file, or is it restricted to single module-specific files?

After reading an intriguing article about the concept of dynamic component loading: I am interested in learning more about the use of System.import. The author demonstrates a specific syntax for loading the JavaScript file of the module that needs to be d ...

Encountering the message "Cannot access undefined properties" when attempting to add a new comment

An error message pop-ups when trying to add a comment Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'fullName') Source components\Comment\index.tsx (53:50) @ fullName 51 | <div className={styles. ...

Eliminate all citation markers in the final compiled result

Currently, I am consolidating all my .ts files into a single file using the following command: tsc -out app.js app.ts --removeComments This is based on the instructions provided in the npm documentation. However, even after compilation, all reference tag ...