When set connectionName to useClass, Populate Error: Schema hasn't been registered for model
Closed this issue · 1 comments
virames-tr commented
Is there an existing issue for this?
- I have searched the existing issues
Current behavior
App Module
import { Module } from '@nestjs/common';
import { MasterModule } from './master/master.module';
import { CoreModule } from './core/core.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { MasterConnection } from './config/master.connection';
import { ConnectionName } from './utils/connectionn.enum';
import { CoreConnection } from './config/core.connection';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
MongooseModule.forRootAsync({
useClass: MasterConnection,
connectionName: ConnectionName.MASTER,
}),
MongooseModule.forRootAsync({
useClass: CoreConnection,
connectionName: ConnectionName.CORE,
}),
MasterModule,
CoreModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
Master Config Service
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
MongooseModuleOptions,
MongooseOptionsFactory,
} from '@nestjs/mongoose';
@Injectable()
export class MasterConnection implements MongooseOptionsFactory {
constructor(private configService: ConfigService) {}
createMongooseOptions(): MongooseModuleOptions {
return {
uri: this.configService.getOrThrow('MONGODB_URI'),
dbName: this.configService.getOrThrow('MASTER_DB'),
connectionFactory(connection: any, name: any) {
connection.plugin(require('mongoose-autopopulate'));
return connection;
},
};
}
}
Core Config Service
import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
import {
MongooseModuleOptions,
MongooseOptionsFactory,
} from '@nestjs/mongoose';
import { TenantService } from 'src/master/tenant/services/tenant.service';
@Injectable({ scope: Scope.REQUEST })
export class CoreConnection implements MongooseOptionsFactory {
constructor(
private configService: ConfigService,
private tenantService: TenantService,
@Inject(REQUEST) private readonly request: Request,
) {}
async createMongooseOptions(): Promise<MongooseModuleOptions> {
const host = this.request.hostname.split('.');
const tenant = await this.tenantService.find({ slug: host[0] });
if (tenant) {
return {
uri: this.configService.getOrThrow('MONGODB_URI'),
dbName: host[0],
lazyConnection: true,
connectionFactory(connection: any, name: any) {
connection.plugin(require('mongoose-autopopulate'));
return connection;
},
};
} else {
throw new NotFoundException('Tenant not found!');
}
}
}
Auth Module
import { Global, Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './entities/user.entity';
import { ConnectionName } from 'src/utils/connectionn.enum';
import { UserController } from './controllers/user.controller';
import { UserService } from './services/user.service';
import { AuthController } from './controllers/auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Role, RoleSchema } from './entities/role.entity';
import { RoleService } from './services/role.service';
import { RoleController } from './controllers/role.controller';
@Global()
@Module({
imports: [
MongooseModule.forFeature(
[
{ name: Role.name, schema: RoleSchema },
{ name: User.name, schema: UserSchema },
],
ConnectionName.CORE,
),
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
global: true,
secret: configService.getOrThrow('JWT_SECRET'),
signOptions: {
expiresIn: configService.getOrThrow('JWT_EXPIRES_IN'),
},
}),
inject: [ConfigService],
}),
],
controllers: [AuthController, RoleController, UserController],
providers: [RoleService, UserService],
exports: [RoleService, UserService],
})
export class AuthModule {}
User Entity
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Schema as MongooseSchema } from 'mongoose';
import { Role } from './role.entity';
@Schema({
versionKey: false,
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at',
},
})
export class User extends Document {
@Prop()
username: string;
@Prop()
password: string;
@Prop()
email: string;
@Prop()
name: string;
@Prop({
type: [MongooseSchema.Types.ObjectId],
ref: Role.name,
autopopulate: true,
})
roles: Role[];
}
export const UserSchema = SchemaFactory.createForClass(User);
Minimum reproduction code
https://github.com/virames-tr/backend
Steps to reproduce
- npm i
- npm start:dev
- http://localhost:3000/api/v1/users
...see error
Expected behavior
I'm trying to set up a multi-tenant system. A different database will be used for each tenant. I shared my sample codes above. When I do it with useClass, the popuplate operation gives an error. If I connect with useFactory, this error does not occur. What could I be doing wrong or could this be a bug?
Package version
10.0.2
mongoose version
8.0.0
NestJS version
10.0.0
Node.js version
18.18.2
In which operating systems have you tested?
- macOS
- Windows
- Linux
Other
No response
kamilmysliwiec commented
Please, use our Discord channel (support) for such questions. We are using GitHub to track bugs, feature requests, and potential improvements.