Reputation: 363
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
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