Li Zhenxin
Li Zhenxin

Reputation: 363

In NestJS, service is invalid when injected to another service?

I`d like to develop a rest app by NestJS and MikroOrm. And I prefer to use clean architecture to manage code, so a lot of modules are abstracted and a module need to be import to another module.

What make me puzzled is that, when service A is injected to service B, the attribute in service A is invalid, but when I use service A directly, it is valid

The more concise describtion is :

Controller C <- Service B (in module B) <- Service A (in module A) : no
Controller C <- Service A (in module A) : yes

Code is:

mikro module:

// mikro-base.dao.ts
export class MikroBaseDao<T extends object> implements IBaseDao<T> {
    constructor(private repo: EntityRepository<T>) {  --> when injected to another service,throw Exception: undefined
    }
    async create(item: T): Promise<T> {
        ...
    }
}

// mikro-business.dao.ts
@Injectable()
export class MikroBusinessDao implements IBusinessDao, OnApplicationBootstrap {
    userAuth: IBaseDao<UserAuthEntity>;
    constructor(
        @InjectRepository(UserAuthEntity)
        private _authRepo: EntityRepository<UserAuthEntity>,
    ) {}
    onApplicationBootstrap() {
        this.userAuth = new MikroBaseDao(this._authRepo);
    }
}

// mikro.module.ts
@Module({
    imports: [
        MikroOrmModule.forRoot(mikroConfig),
        MikroOrmModule.forFeature({
          entities: [UserAuthEntity],
        }),
      ],
    providers: [
        {
          provide: IBusinessDao.DI,
          useClass: MikroBusinessDao,
        },
      ],
    exports: [IBusinessDao.DI],
})
export class MikroDatabaseModule {}

// mikro.config.ts
export const mikroConfig: Options = {
  host: 'localhost',
  port: 3306,
  user: 'root',
  password: 'passwd',
  dbName: 'db',
  entities: [BaseEntity, UserAuthEntity],
  debug: true,
  highlighter: new SqlHighlighter(),
  logger: logger.log.bind(logger),
  driver: MariaDbDriver,
};

// database.module.ts
@Module({
  imports: [MikroDatabaseModule],
  exports: [MikroDatabaseModule],
})
export class DatabaseModule {}

MikroDatabaseModule is injected to AuthServiceModule:

// auth.service.ts
@Injectable()
export class AuthService implements IAuthService {
  constructor(
    @Inject(IBusinessDao.DI) private dao: IBusinessDao,
    private jwtService: JwtService,
  ) {}

    async createAuth(phone: string, userId: string): Promise<UserAuthEntity> {
        const payload = { sub: userId, phone: phone };
        return this.jwtService
          .signAsync(payload)
          .then(
            (token) => new UserAuthEntity(0, userId, phone, token, Status.Normal),
          )
          .then(this.dao.userAuth.create);
      }
}
// auth-service.module.ts
@Module({
  imports: [
    DatabaseModule,
    JwtModule.register({
      global: true,
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '7day' },
    }),
  ],
  providers: [AuthService],
  exports: [AuthService, DatabaseModule],
})
export class AuthServiceModule {}

and AuthServiceModule is injected to AuthModule:

// auth.controller.ts
@Controller('auth')
export class AuthController {
  constructor(
    private auth: AuthService,
    @Inject(IBusinessDao.DI) private dao: IBusinessDao,
  ) {}

  @AllowAnon()
  @Post('login/code')
  async login(@Body() req: LoginRequest): Promise<UserAuth> {
    return this.implByService(req);
    // return this.implByDao(req);
  }
  // invalid
  private async implByService(req: LoginRequest) {
    let existAuth = await this.auth.queryAuth(req.phone);
    console.log(`exist auth: ${JSON.stringify(existAuth)}`);
    if (existAuth === null) {
      existAuth = await this.auth.createAuth(req.phone, 'sdfsdfsd');
      console.log(`create new auth: ${JSON.stringify(existAuth)}`);
    }
    return createUserAuth(existAuth);
  }

  // ok
  private async implByDao(req: LoginRequest) {
    let existAuth = await this.dao.userAuth.get('phone', req.phone);
    console.log(`exist auth: ${JSON.stringify(existAuth)}`);
    if (existAuth === null) {
      const authEntity = new UserAuthEntity(
        1,
        req.phone,
        'sfdsdf',
        'sfdsdf',
        Status.Normal,
      );
      existAuth = await this.dao.userAuth.create(authEntity);
      console.log(`create new auth: ${JSON.stringify(existAuth)}`);
    }
    return createUserAuth(existAuth);
  }
}

// auth.module.ts
@Module({
  imports: [AuthServiceModule],
  controllers: [AuthController],
})
export class AuthModule {}
// app.module.ts
@Module({
  imports: [ApiModule],
})
export class AppModule {}

In the AuthController, I have two implements of login: implByService() use a AuthService with dao injected, and another, implByDao() use directly dao.

implByDao() run correctly, BUT implByService() run with exception:

TypeError: Cannot read properties of undefined (reading 'repo')                                                                                                                            
  at create (/home/serveradmin/mrmap/server/mainline/src/iac/database/mikro/dao/mikro-base.dao.ts:26:17)                                                                                            
  at processTicksAndRejections (node:internal/process/task_queues:95:5)                                                                                                                             
  at async AuthController.implByService (/home/serveradmin/mrmap/server/mainline/src/api/auth/auth.controller.ts:31:19)

I check the DI, it seems no problem. so how can I fit this fxck trouble?

Upvotes: 0

Views: 41

Answers (1)

Li Zhenxin
Li Zhenxin

Reputation: 363

emmm.........matter is over, this bug is caused by a funny code style:

@Injectable()
export class AuthService implements IAuthService {
  constructor(
    @Inject(IBusinessDao.DI) private dao: IBusinessDao,
    private jwtService: JwtService,
  ) {}

    async createAuth(phone: string, userId: string): Promise<UserAuthEntity> {
        const payload = { sub: userId, phone: phone };
        return this.jwtService
          .signAsync(payload)
          .then(
            (token) => new UserAuthEntity(0, userId, phone, token, Status.Normal),
          )
          // .then(this.dao.userAuth.create); -->> this line cause bug
          .then((it) => this.dao.userAuth.create(it)); -->> should be this
      }
}

should blame to I am new to Typescript.

so who can show me the difference between two lines?

Upvotes: 0

Related Questions