Nest can't resolve DataSource as dependency

Viewed 5564

I've had a problem with dependencies in NestJS. On launch my NestJS app, compiler throw me this:

[Nest] 16004  - 09.04.2022, 16:14:46   ERROR [ExceptionHandler] Nest can't resolve dependencies of the AccountService (MailingService, ?). Please make sure that the argument DataSource at index [1] is available in the AccountModule context.

Can someone tell me what I'm doing wrong?

Here is my modules:

@Module({
  imports: [],
  providers: [AccountService, MailingService],
  controllers: [AccountController],
  exports: []
})
export class AccountModule {}

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useClass: TypeOrmConfigService,
    }),
  ],
})
export class DatabaseModule {}

@Module({
  imports: [
    DatabaseModule,
    ConfigModule.forRoot({
      envFilePath: '.env',
    }),
    ScheduleModule.forRoot(),
    AccountModule,
    AuthModule,
    MailingModule],
  controllers: [AppController],
  providers: [],
})
export class AppModule { }

thanks for any help!

PS. DataSource is a class from TypeORM to make query, earlier it was Connection class

6 Answers

I created my own TypeOrmModule:

import { Global, Module } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { typeOrmConnectionDataSource } from './typeorm-datasource';

@Global()
@Module({
  imports: [],
  providers: [
    {
      provide: DataSource,
      useFactory: async () => {
        await typeOrmConnectionDataSource.initialize();
        return typeOrmConnectionDataSource;
      },
    },
  ],
  exports: [DataSource],
})
export class TypeOrmModule {}

Here is "./typeorm-datasource" (but replace by your configuration):

import { DataSource } from "typeorm"

export const typeOrmConnectionDataSource = new DataSource({
  name: "default",
  type: "postgres",
  host: "localhost",
  port: 15432,
  username: "postgres",
  password: "postgres",
  database: "postgres",
  synchronize: false,
  schema: "my-schema",
  migrationsRun: false,
  entities: [`${__dirname}/**/**.entity{.ts,.js}`],
  migrations: [`${__dirname}/migrations/**/*{.ts,.js}`],
});

The first step we have to create database providers.

database.providers.ts

import { DataSource } from 'typeorm';

export const databaseProviders = [
    {
        provide: DataSource,
        useFactory: async () => {
            // You can inject config service to provide dynamic DataSourceOptions
            const dataSource = new DataSource({
                type: 'mysql',
                host: 'localhost',
                port: 3306,
                username: 'root',
                password: 'root',
                database: 'test',
                entities: [
                    __dirname + '/../**/*.entity{.ts,.js}',
                ],
                synchronize: true,
            });
            try {
                if (!dataSource.isInitialized) {
                    await dataSource.initialize();
                }
            } catch (error) {
                console.error(error?.message);
            }
            return dataSource;
        }
    }
];

Now we can import/export our database providers inside DatabaseModule.

database.module.ts

import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';

@Module({
    providers: [...databaseProviders],
    exports: [...databaseProviders],
})
export class DatabaseModule {}

Now, We can Inject DataSouce dependency into any feature module. i.e. photo.module.ts

Note: Do not forgot to import DatabaseModule inside PhotoModule.

The correct approach is to provide a Custom DataSource Factory as the official documentation explains here.

 TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      // Use useFactory, useClass, or useExisting
      // to configure the DataSourceOptions.
      useFactory: (configService: ConfigService) => ({
        type: 'mysql',
        host: configService.get('HOST'),
        port: +configService.get('PORT'),
        username: configService.get('USERNAME'),
        password: configService.get('PASSWORD'),
        database: configService.get('DATABASE'),
        entities: [],
        synchronize: true,
      }),
      // dataSource receives the configured DataSourceOptions
      // and returns a Promise<DataSource>.
      dataSourceFactory: async (options) => {
        const dataSource = await new DataSource(options).initialize();
        return dataSource;
      },
    });

The docs are not very clear on this, but i figured out you can use the @InjectDataSource() decorator from @nest/typeorm package.

example:

import { DataSource } from 'typeorm';
import { InjectDataSource } from '@nestjs/typeorm';

@Module({
  imports: [TypeOrmModule.forRoot(), UsersModule],
})
export class AppModule {
  constructor(
    @InjectDataSource()
    private dataSource: DataSource
  ) {}
}

I resolve it, the problem was in DataSource class, the docs tell us that: DataSource is a pre-defined connection configuration to a specific database. You can have multiple data sources connected (with multiple connections in it), connected to multiple databases in your application.. So, we can't use this class as a provider to make queries to our entites.

Inthe earlier versions of the TypeORM we can use Connection class, but right now the best choice is to use EntityManager instaed of DataSource or deprecated Connection

You need to export AccountService

@Module({
  imports: [],
  providers: [AccountService, MailingService],
  controllers: [AccountController],
  exports: [AccountService] // here
})
export class AccountModule {}
Related