Alex
Alex

Reputation: 1381

Syntax for chaining mongoose promises

I am relatively new to using Promises and MongoDB / Mongoose, and am trying to chain a flow through several database queries into an efficient and reliable function.

I want to know if my final function is a good and reliable way of achieving what I want, or if there are any issues or any improvements that can be made.

The process is as follows:

1) Check whether or not the user already exists

usersSchema.findOne({
    email: email
}).then(res => {
    if(res==null){
        // user does not exist
    }
}).catch(err => {});

2) Add the new user to the database

var new_user = new usersSchema({ email: email });
new_user.save().then(res => {
    // new user id is res._id
}).catch(err => {});

3) Assign a free promotional code to the user

codeSchema.findOneAndUpdate({
    used: false,
    user_id: true
},{
    used: true,
    user_id: mongoose.Types.ObjectId(res._id)
}).then(res => {
    // user's code is res.code
}).catch(err => {});

Obviously, each query needs to execute in sequence, so after a lot of research and experimentation into how to do this I have combined the queries into the following function, which seems to be working fine so far:

function signup(email){
    // check email isn't already signed up
    return usersSchema.findOne({
        email: email
    }).then(res => {
        if(res==null){
            // add to schema
            var new_user = new usersSchema({ email: email });
            // insert new user
            return new_user.save().then(res => {
                var result = parse_result(res);
                // assign a code
                return codesSchema.findOneAndUpdate({
                    used: false,
                    user_id: true
                },{
                    used: true,
                    user_id: mongoose.Types.ObjectId(result._id),
                });
            });
        }else{
            return 'The user already exists';
        }
    });
}

signup('[email protected]').then(res => {
    console.log('success, your code is '+res.code);
}).catch(err => {
    console.log(err);
});

I'm still trying to get my head around exactly how and why this works - the function is returning a promise, and each nested promise is returning a promise.

My main concerns are that there is a lot of nesting going on (is there perhaps a way to do this by chaining .then() callbacks instead of nesting everything?) and that the nested promises don't appear to have error catching, although as the signup() function itself is a promise this seems to catch all the errors.

Is anyone knowledgeable on the subject able to confirm whether my process looks good and reliable or not? Thanks!

Upvotes: 2

Views: 2698

Answers (2)

Sven
Sven

Reputation: 5265

To avoid indentation-hell, if you return a value from the function passed to a Promise's .then()-method, you can chain multiple .then() as a neat and tidy pipeline. Note that you can also return a Promise that has pending status, and then next function in line will execute when it has resolved.

function signup (email) {
  return usersSchema.findOne({
    email: email
  }).then(res => {
    if (res) throw 'The user already exists'
    var new_user = new usersSchema({ email: email })
    return new_user.save()
  }).then(res => {
    var result = parse_result(res)
    return codesSchema.findOneAndUpdate({
      used: false,
      user_id: true
    },{
      used: true,
      user_id: mongoose.Types.ObjectId(result._id)
    })
  })
}

Even better, if you have the possibility to use async/await (Node v7.6 or above), your code can look like normal blocking code:

async function signup (email) {
  let user = await usersSchema.findOne({ email: email })
  if (user) throw 'The user already exists'
  let new_user = await new usersSchema({ email: email }).save()
  let result = parse_result(new_user)
  return codesSchema.findOneAndUpdate({
    used: false,
    user_id: true
  },{
    used: true,
    user_id: mongoose.Types.ObjectId(result._id)
  })
}

Your original function call code works on both without changes.

Upvotes: 4

Ricardo
Ricardo

Reputation: 2487

you code can be improve in this way

function signup(email){
    // check email isn't already signed up
    return usersSchema.findOne({
        email: email
    }).then(res => {
        if(res==null){            // add to schema
            var new_user = new usersSchema({ email: email });
            // insert new user
            return new_user.save()
        }else{
            return Promise.reject(new Error('The user already exists'));
        }
    })
    .then(res => {
        var result = parse_result(res);
        // assign a code
        return codesSchema.findOneAndUpdate({used: false,user_id: true},{used: true,user_id: mongoose.Types.ObjectId(result._id),});
    });

}

signup('[email protected]').then(res => {
    console.log('success, your code is '+res.code);
}).catch(err => {
    console.log(err);
});

Upvotes: 2

Related Questions