Nest: What is the best method of providing mongoose schemas in order to populate() ?

Created on 19 Jan 2018  路  4Comments  路  Source: nestjs/nest

I'm submitting a...


[ ] Regression 
[X ] Bug report
[ ] Feature request
[ ] Documentation issue or request
[X ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

I have the following component, and am trying to populate fields from related models in the API response. However I get this error:

[Nest] 5581   - 2018-1-19 14:04:53   [ExceptionsHandler] Schema hasn't been registered for model "Material".
Use mongoose.model(name, schema)
MissingSchemaError: Schema hasn't been registered for model "Material".
Use mongoose.model(name, schema)
    at new MissingSchemaError 

I have tried creating modules for for Size and Material, and importing them into Products module, but get the same issue. Any guidance would be much appreciated:)

Controller:

@Component()
export class ProductsService {
    constructor(@InjectModel(ProductSchema) private readonly productModel: Model<Product>) {
    }

    async create(createProductDto: CreateProductDto): Promise<Product> {
        const createdProduct = new this.productModel(createProductDto);
        return await createdProduct.save();
    }

    async findAll(): Promise<Product[]> {
        return await this.productModel.find()
            .populate('material')
            .populate('size')
            .exec();
    }
}

and module:

@Module({
    imports: [
        MongooseModule.forFeature([{name: 'Product', schema: ProductSchema}]),
    ],
    controllers: [ProdcutsController],
    components: [ProductsService]
})
export class ProdcutsModule {
}

and Products Schema:

export const ProductSchema = new mongoose.Schema({
    name: String,
    description: String,
    size: {type: mongoose.Schema.Types.ObjectId, ref: 'Size'},
    material: {type: mongoose.Schema.Types.ObjectId, ref: 'Material'},
    range: {type: mongoose.Schema.Types.ObjectId, ref: 'Range'}
}, {
    timestamps: true
});

Environment


Nest version: 4.5.8

For Tooling issues:

  • Node version: 9.20
  • Platform: Ubuntu

Most helpful comment

Just contributing for future finders, you can also import the Model at the module level like so

@Module({
  imports: [MongooseModule.forFeature([
    {name: 'Product', schema: ProductSchema}
    {name: 'Size', schema: SizeSchema}, // adding SizeSchema here
    {name: 'Material', schema: MaterialSchema}, // adding MaterialSchema here
  ])],
  controllers: [ProdcutsController],
  components: [ProductsService]
})
export class ProductsModule {}

which will allow you to inject it into your ProductsService provider/component like so:

@Component()
export class ProductsService {
  constructor(
    @InjectModel('ProductSchema') private readonly productModel: Model<Product>
    @InjectModel('SizeSchema') private readonly sizeModel: Model<Size> // inject Size here
    @InjectModel('MaterialSchema') private readonly materialModel: Model<Material> // inject Material here
  ) {}

  async create(createProductDto: CreateProductDto): Promise<Product> {
    const createdProduct = new this.productModel(createProductDto);
    return await createdProduct.save();
  }

  async findAll(): Promise<Product[]> {
    return await this.productModel.find()
      .populate('material', this.materialModel) // you can even reference the injections here if you need
      .populate('size', this.sizeModel)
      .exec();
  }
}

All 4 comments

Ended up adding the schemas :

    async findOne(id: number): Promise<Product[]> {

        const Size = mongoose.model('Size', SizeSchema);
        const Material = mongoose.model('Material', MaterialSchema);

        return await this.productModel.findById(id)
            .populate({
                path: 'size',
                model: Size
            })
            .populate({
                path: 'material',
                model: Material
            })
            .exec();
    }

Closing this, as I think its more of an issue with me being unfamilar with mongoose

Just contributing for future finders, you can also import the Model at the module level like so

@Module({
  imports: [MongooseModule.forFeature([
    {name: 'Product', schema: ProductSchema}
    {name: 'Size', schema: SizeSchema}, // adding SizeSchema here
    {name: 'Material', schema: MaterialSchema}, // adding MaterialSchema here
  ])],
  controllers: [ProdcutsController],
  components: [ProductsService]
})
export class ProductsModule {}

which will allow you to inject it into your ProductsService provider/component like so:

@Component()
export class ProductsService {
  constructor(
    @InjectModel('ProductSchema') private readonly productModel: Model<Product>
    @InjectModel('SizeSchema') private readonly sizeModel: Model<Size> // inject Size here
    @InjectModel('MaterialSchema') private readonly materialModel: Model<Material> // inject Material here
  ) {}

  async create(createProductDto: CreateProductDto): Promise<Product> {
    const createdProduct = new this.productModel(createProductDto);
    return await createdProduct.save();
  }

  async findAll(): Promise<Product[]> {
    return await this.productModel.find()
      .populate('material', this.materialModel) // you can even reference the injections here if you need
      .populate('size', this.sizeModel)
      .exec();
  }
}

Why do they call it a product model? It is a scheme as such, the models do not have to do with databases, an entity yes, a scheme yes, but a model does not ?

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cojack picture cojack  路  3Comments

menme95 picture menme95  路  3Comments

artaommahe picture artaommahe  路  3Comments

yanshuf0 picture yanshuf0  路  3Comments

marshall007 picture marshall007  路  3Comments