5

When using the waterline ORM, if I want to consume the bluebird promise api thats shipped by default how to I pass the processing back to the controller.

Below is the code :

module.exports = {
    //Authenticate
    auth: function (req, res) {
        user = req.allParams();
        //Authenticate
        User.authenticate(user, function (response) {
            console.log(response);
            if (response == true) {
                res.send('Authenticated');
            } else {
                res.send('Failed');
            }
        });
    }
};


module.exports = {
    // Attributes

    // Authenticate a user
    authenticate: function (req, cb) {
        User.findOne({
            username: req.username
        })
        .then(function (user) {
            var bcrypt = require('bcrypt');
            // check for the password
            bcrypt.compare(req.password, user.password, function (err, res) {
                console.log(res);
                if (res == true) {
                    cb(true);
                } else {
                    cb(false);
                }
            });
        })
        .catch(function (e) {
            console.log(e);
        });
    }
};

I am simply trying to implement a authentication function. The business logic is straight forward. What I am confused of is how the request flow is handed back to the controller since. The promise doesn't respond if I try to return a response, but doing a cb(value) works.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
Gayan Hewa
  • 2,277
  • 4
  • 22
  • 41
  • Where are you returning Promise in this code? – vanadium23 Jun 05 '15 at 05:58
  • According to the doc (http://sailsjs.org/#!/documentation/reference/waterline/queries) , waterline has a partial implementation of Bluebird , I am having wrapping my head around the code after the User.find....then – Gayan Hewa Jun 05 '15 at 06:03

1 Answers1

6

The key to working with promises is to never break the chain. A promise chain depends on every step either returning a promise or a value, or throwing an error.

The following is a rewrite of your code. Note that

  • Every callback in the path returns something and every function returns the promise chain it works with (even .auth(); it might be useful at some point)
  • I've used BlueBird's .promisifyAll() to make bcrypt play along
  • I've decoupled .authenticate() from your request/response infrastructure by making the username and password arguments explicit. This way it can be reused more easily.

So now we have (not 100% tested, I did not bother installing waterline):

module.exports = {
    // authenticate the login request
    auth: function (req, res) {
        var params = req.allParams();
        return User.authenticate(params.username, params.password)
        .then(function () {
            res.send('Authenticated');
        })
        .fail(function (reason) {
            res.send('Failed (' + reason + ')');
        });
    }
};

and

var Promise = require("bluebird");
var bcrypt = Promise.promisifyAll(require('bcrypt'));

module.exports = {
    // check a username/password combination
    authenticate: function (username, password) {
        return User.findOne({
            username: username
        })
        .then(function (user) {
            return bcrypt.compareAsync(password, user.password)
        })
        .catch(function (err) {
            // catch any exception problem up to this point
            console.log("Serious problem during authentication", err);
            return false;
        })
        .then(function (result) {
            // turn `false` into an actual error and
            // send a less revealing error message to the client
            if (result === true) {
                return true;
            } else {
                throw new Error("username or password do not match");
            }
        });
    }
};
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 2
    this clears a lot of questions I had about promises. +1 – Gayan Hewa Jun 06 '15 at 10:24
  • 2
    They take some getting used to, but after that they are a beautiful tool. Better in every way, compared to node's native callback architecture. – Tomalak Jun 06 '15 at 10:29
  • Should this not also return a promise instead of true/false? I suppose otherwise there is no actual callback you could do in a Controller, or somewhere at the http level so this would be then a sync call. – mkbrv Sep 28 '17 at 20:24
  • A promise doesn't have to return a promise. You can return a value directly, that's the whole point of it. I don't quite understand what you mean with "otherwise there is no callback"? – Tomalak Sep 29 '17 at 07:01