Mongoose: Unable to make an array of ref required

Created on 28 Jul 2020  路  4Comments  路  Source: Automattic/mongoose

Do you want to request a feature or report a bug?
Might be a bug

What is the current behavior?

const TodoSchema: Schema = new Schema({
    name: { type: String, required: true },
    items: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Item',
        required: true
    }]
})
export default mongoose.model<ITodo>("Todo", TodoSchema)
const todo = new Item({
     name: "My todo"
})

await expect(todo.validate()).rejects.toThrow("Todo validation failed: items: Path `items` is required.")

The test fails (last line), because validation is not enforced. Here's the failure:

    expect(received).rejects.toThrow()

    Received promise resolved instead of rejected
    Resolved to value: undefined

If the current behavior is a bug, please provide the steps to reproduce.
See the code above

What is the expected behavior?
The test fails because an array of items isn't provided

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node: 12.x
Mongoose: 5.9.25
MongoDB: 3.6

help

Most helpful comment

You should define your schema as below

const mongoose = require("mongoose")
mongoose.set('debug', true);

const connectionString = 'mongodb://localhost:27017/test_db';
console.log('version:: ', mongoose.version)
run()
    .then(() => console.log('done'))
    .catch(error => console.error(error))
    .finally(() => {
        process.exit(1)
    })

async function run() {
    await mongoose.connect(connectionString);
    await mongoose.connection.dropDatabase();
    mongoose.model('Item', new mongoose.Schema({ name: { type: String } }));
    const schema = new mongoose.Schema({
        name: { type: String, required: true },
        items: {
            type: [{
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Item',
                required: true
            }],
            validate: {
                validator: function () {
                    return this.items && this.items.length > 0
                },
                message:() => `items should not be empty`
            }
        }
    })
    const M = mongoose.model('Test', schema);
    const data = { name: 'abc' }
    await M.create(data)
}

All 4 comments

You should define your schema as below

const mongoose = require("mongoose")
mongoose.set('debug', true);

const connectionString = 'mongodb://localhost:27017/test_db';
console.log('version:: ', mongoose.version)
run()
    .then(() => console.log('done'))
    .catch(error => console.error(error))
    .finally(() => {
        process.exit(1)
    })

async function run() {
    await mongoose.connect(connectionString);
    await mongoose.connection.dropDatabase();
    mongoose.model('Item', new mongoose.Schema({ name: { type: String } }));
    const schema = new mongoose.Schema({
        name: { type: String, required: true },
        items: {
            type: [{
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Item',
                required: true
            }],
            validate: {
                validator: function () {
                    return this.items && this.items.length > 0
                },
                message:() => `items should not be empty`
            }
        }
    })
    const M = mongoose.model('Test', schema);
    const data = { name: 'abc' }
    await M.create(data)
}

@bhrigushr is right. Mongoose arrays implicitly have a default value of []: https://mongoosejs.com/docs/schematypes.html#arrays so if you don't specify items, you'll get an empty array.

An alternative would be to make your array undefined by default:

const TodoSchema: Schema = new Schema({
    name: { type: String, required: true },
    items: {
      // `items` is an array of non-nullable ObjectIds which is `undefined` by default
      type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Item', required: true }]
      default: void 0
    }
})

See more info on void 0

Thanks, @bhrigushr's solution worked. That makes sense, but maybe it could be a bit more easier / straightforward.
@vkarpov15 I wasn't able to make that solution work. Too bad, because I like this conciseness.

Thanks both of you for your help anyway

@afillatre you're right, I made a typo in how I defined my schema. Fixed :+1:

Was this page helpful?
0 / 5 - 0 ratings