ActionHero, Sequelize & Promises

189 views
Skip to first unread message

Paul Tiseo

unread,
Mar 11, 2015, 5:11:41 PM3/11/15
to action...@googlegroups.com
I don't seem to be triggering an error message in the response with this code. I use Sequelize to fetch one record, then another. Not sure I am using promises correctly, and I am not returning any error messages back to the browser. Some advanced example of ActionHero used with promises, esp. doing multiple async actions like database actions would be great. Or, I'd appreciate if someone could give me a code review...

if(config.protocol == 'local') {

 
// check if we have a username and password to verify
 
var username = params[config.options.userField];
 
var password = params[config.options.pwdField];
 
if(username && password) {
   
return api.models.operator.find({where: {username: username}})
     
.then(function(operator) {
       
if(!operator) {
         
throw 'no username/password match 1';
       
}
       
return operator;
     
})
     
.then(function(operator) {
       
return api.models.credential.find({where: { operator_id: operator.get('id')}})
         
.then(function(cred) {
           
if(!cred) {
             
throw 'no username/password match 2';
           
}

           
if( cred.verifyPassword(password) ) {
             
// do stuff
           
}
           
else {
             
throw 'no username/password match 3';
           
}
         
})
         
.catch(function(e) {
            connection
.error = 'error fetching credential for: ' + operator.get('id') +'\nmessage: ' + e;
         
});
     
})
     
.catch(function(e) {
        connection
.error = 'error fetching operator: ' + username +'\nmessage: ' + e;
     
})
     
.finally(next(connection,true));
 
}
 
else {
    connection
.error = 'missing username and/or password';
 
}

}

return next(connection, true);


Paul Tiseo

unread,
Mar 11, 2015, 10:02:59 PM3/11/15
to action...@googlegroups.com
Fixed it. For posterity's sake, as opposed to my previous post, I didn't wrap next() in a function in the finally().

i
f(config.protocol == 'local') {

 
// check if we have a username and password to verify
 
var username = params[config.options.userField];
 
var password = params[config.options.pwdField];
 
if(username && password) {
   
return api.models.operator.find({where: {username: username}})
     
.then(function(operator) {

       
//console.log(operator);

       
if(!operator) {
         
throw 'no username/password match 1';
       
}
       
return operator;
     
})
     
.then(function(operator) {

       
return api.models.credential.find({where: { operator_id: operator.get('id')}});
     
})
     
.then(function(cred) {
       
//console.log(cred);

       
if(!cred) {
         
throw 'no username/password match 2';
       
}

       
if(cred.verifyPassword(password)) {

         
// create cookie
          console
.log('we good!');

       
}
       
else {
         
throw 'no username/password match 3';
       
}
     
})
     
.catch(function(e) {

       
//console.log(e);
        connection
.error = e; // TODO: user vs system error
     
})
     
.finally(function() {
       
next(connection,true);

Evan Tahler

unread,
Mar 12, 2015, 6:10:24 AM3/12/15
to action...@googlegroups.com
Cool! 

I haven't brought any promises into actionhero's core (I'm holding out for ES6), but I'm happy to see that everything works! 

Clay Himmelberger

unread,
Mar 12, 2015, 9:05:37 AM3/12/15
to action...@googlegroups.com
Just FYI, the internal libraries of ActionHero are easily Promisified. I used the bluebird package (which was also used by my ORM).

I used a Promise initializer similar to this:
'use strict';

module.exports = {
  loadPriority: 990,
  startPriority: 1000,
  stopPriority: 1000,
  initialize: function( api, next ) {

    api.Promise = require( 'bluebird' );

    api.Promise.promisifyAll( api.chatRoom );
    api.Promise.promisifyAll( api.tasks );

    next();
  },
  start: function( api, next ) {
    next();
  },
  stop: function( api, next ) {
    next();
  }
}

So I could access the Promise library anywhere with `api.Promise`. But also, my chat or task calls used promises nicely. Something like this (in my `chatLogic` initializer):

    api.chatLogic.createAndJoinRoom = function( connection, roomId, extraInfo ) {
      return api.chatRoom.addAsync( roomId )
        .then( function( count ) {
          api.log( '...finished adding room, now adding member. count=' + count, api.chatLogic.logLevel );
          if( !count ) {
            api.log( 'Error initializing room [' + roomId + '].' + ( !!extraInfo ? ' info=' + JSON.stringify( extraInfo ) : '' ), 'error' );
            throw new Error( 'Could not initialize room.' );
          }
          //Room has been added, now join it
          return api.chatRoom.addMemberAsync( connection.id, roomId );
        } )
        .then( function( added ) {
          api.log( '...member added to room. Done! added=' + added, api.chatLogic.logLevel );
          //Should always be added at this point. No other members since room just joined.
          return {
            success: added,
            roomId: roomId,
            members: []
          };
        } );
    };


Chad Robinson

unread,
Mar 24, 2015, 3:14:47 PM3/24/15
to action...@googlegroups.com
We do the same thing Clay noted here. Very good results with it. For the curious, we benchmarked to measure the overhead but it was almost unmeasurable - way less than the transaction rates our Web heads can handle socket-wise in the first place.

We used a mix of kristowal's 'q' package and the 'async' package (which has a super nice asyncmap option for doing series/parallel work on multiple tasks), but Bluebird is also great. They're compatible - our ORM also uses Bluebird and we can chain right from one to the other.

Paul Tiseo

unread,
May 6, 2015, 1:07:35 PM5/6/15
to action...@googlegroups.com
Sorry to revive this thread, but hoping to glom on to some other people using promises.

I am using bluebird also, via the fact that Sequelize uses it to promisify database wacces.

Now, if I have an action in AH where the return is a promise (ex: return api.models.operator.find().finally()) where the promise eventually calls next(), is there an issue with the flow completing before the promise satisfies or fails, and next() never resulting in a response back to the external user?

Clay Himmelberger

unread,
May 6, 2015, 1:27:59 PM5/6/15
to action...@googlegroups.com
The actions in AH need to call next() to complete. So the end of your Promise chain must call `next()`, either inside the final `.then()`, the last `.catch()` or a `.finally()` (but only use finally if you have to, not as a safety net . . . it will hide things you should know are happening).

If I read your question right, somehow your action is returning a promise, and the user is not getting a response? That doesn't sound right, like you're messing with AH too much. AH action's use the next() callback, and should send something to the caller. I guess it could work another way, but does not seem how AH was intended to be used, which makes me want to ask more about your use case  . . .

. . . so I will!  Tell us more about your use case? : )

Clay

Paul Tiseo

unread,
May 6, 2015, 4:41:20 PM5/6/15
to action...@googlegroups.com
Actually, I haven't had a problem with returning promises...so far. ;) However, I wasn't sure if there wasn't some problem with that design lurking in my code that other people would have a perspective to share with me...

Evan Tahler

unread,
May 6, 2015, 5:17:41 PM5/6/15
to action...@googlegroups.com, paulx...@gmail.com

Everything you want to do should be fine... just `next.apply()` at some point :D  

Hopefully the new `data` object in actionhero v11 should make this easier because there are less objects you'll need to keep in scope. 

Reply all
Reply to author
Forward
0 new messages