NestJS Mongoose Request-Scoped Connection Error/Fail

64 views Asked by At

Imagine for example: I've got two databases with same objects/models but for different Organisations. And based on request I need to select correct DB to connect. I use this in app.module.ts:

MongooseModule.forRootAsync({
  imports: [HttpModule],
  useClass: MyMongooseConfigService,
})

MyMongooseConfigService looks something like this:

@Injectable({ scope: Scope.REQUEST })
export class MyMongooseConfigService implements MongooseOptionsFactory {
  constructor(
    @Inject(REQUEST)
    private readonly req: Request,
    private http: HttpService,
  ) {}

  async createMongooseOptions(): Promise<MongooseModuleOptions> {
    const param = req.headers.mysecretheader;
    const foundDb = await this.http.get(`http://my-web-server/param/${param}`);
    const dbName = foundDb.dbName;
    const uri = `mongodb+srv://connection_data/${dbName}`;
    console.log(`uri for stackoverflow:`, uri);
    return { uri };
  }
}

I've got a web server which returns this Organisation object by HTTP which is stored in 3-rd DB with a structure like:

{
  param: string;
  dbName: string;
}

So, when I get request, I have the param by which to search dbName in Organisations' DB on my web server.

I expect DB connection to use this MyMongooseConfigService per request and every connection will go to appropriate DB. But that's not the case. I've got for example UserService which gets users from correct Organisation. It looks something like:

@Injectable()
export class UserService {
  constructor(
    @InjectModel(User.name)
    private userModel: Model<User> 
  ) {}

  async getUsers() {
    console.log(`dbName for stackoverflow:`, this.userModel.db.name)
    const users = await this.userModel.find();
  }
}

The resulting users don't belong to correct organisation. So, what's wrong?

During initialisation mongoose make a connection attempt without actual request, and finds first available dbName. I've even asked about this "init" connection in official NestJS Discord channel, there I've got the answer: the assumption is, you know what databases are supposed to be in use at the time the app starts.

Web server that returns dbName by param makes search in DB like organisationModel.findOne({}) if no param passed (I guess, it's serialisation when I make a request to web server) and it returns first Organisation in the list. So if I have in Organisations DB objects like:

{ param: '1234', dbName: 'org-1' } and { param: '5678', dbName: 'org-2' }

the first found Organisation's dbName will always be org-1. If I remove all Organisations and add them in another order, the first found will always be org-2

And I've made console.log's for stackoverflow on purpose in UserService and in MyMongooseConfigService. When I make a request with param 5678 I've got in logs:

uri for stackoverflow: mongodb+srv://connection_data/org-2
dbName for stackoverflow: org-1

This is the actual problem: connection uri in MyMongooseConfigService is correct every time, but the model always returns the db.name of the first connected DB. TS says that model.db.name is "The name of the database this connection points to".

Maybe I've done something wrong, forgot something? Can anyone help/guide?

I've tried to add @Injectable({ scope: Scope.REQUEST }) to UserService. Seems like nothing has changed, because it all should bubble up to controller and to the request.

0

There are 0 answers