I am trying to create a multitenant DB with node js and MongoDB, and it works fine, I can create a new database with a tag, for example: main database store, tenant: store_fool, and each tenant has their tables (collections).
So, what's the problem?
I had this model:
const BoardSchema = new Schema<Board, Model<Board>>({
name: {
type: String,
required: true
},
cards: [{
type: SchemaTypes.ObjectId,
ref: 'cards'
}],
color: {
type: String,
default: '#55AC79'
}
})
const BoardModel = async (id: string) => {
try {
const db = await getTenantDb(id);
return db!.model('boards', BoardSchema);
} catch (error) {
console.log(error);
throw error;
}
}
And this is my card model:
const CardSchema = new Schema<Card, Model<Card>>({
name: {
type: String,
required: true
},
description: {
type: String,
required: true
}
})
const CardModel = async (id: string) => {
try {
const db = await getTenantDb(id);
return db!.model('cards', CardSchema)
} catch (error) {
console.log(error);
throw error;
}
}
I tried to get the info on boards with these lines:
const Board = await BoardModel(x_tenant as string);
const data = await Board.find();
And it works fine, if I show the collection on my MongoDB Compass GUI, it sends me the correct collections.
The problem is when I tried to populate cards
const Board = await BoardModel(x_tenant as string);
const data = await Board.find().populate('cards'); //it causes error
I got the following error:
MissingSchemaError: Schema hasn't been registered for model "cards".
But in MongoDB Compass GUI the collection exists, if I don't populate the prop, it works.
Note The
id
of cards inboards
collection exists oncards
collection
I'm not familiar with some of your syntax for creating your models but the MissingSchemaError: Schema hasn't been registered for model "cards".
is thrown by mongoose when it can't find the particular model to use when doing the populate on the field you specified. However, implementing these changes should resolve your issues.
The correct format for referencing your cards
array would be:
cards: [{
type: SchemaTypes.ObjectId,
ref: 'Card' //< Change to this
}],
When you create the Models use the single, title-cased version of your collection names. So this:
return db!.model('Board', BoardSchema); //< Change to this
return db!.model('Card', CardSchema) //< Change to this
With these changes in place it is often the case, depending on your project, that you need to explicitly pass the model name like this:
const data = await Board.find().populate({path: 'cards', model: Card});
See mongoose populate and Populate with TypeScript for further reading.