0

This is the call to a controller in a routes file from a nodejs application running express witha promise based api.

router.post('/create-user', function(req, res) {
        CreateUserController.createUser( req.body.username, req.body.username ).then( res.jsonSuccess, res.jsonFail );
});

As you can see the end result of a resolved callback above hits res.jsonSuccess and the reject (the 2nd param in the then function) hits the res.jsonFail.

res.jsonSuccess & res.jsonFail are custom middlewares for this project and simply format and log data accordingly.

My question: I want to simplify this further. Is it possible to pass one parameter to the then() that could handle both the resolve and reject?

For example

router.post('/create-user', function(req, res) {
        CreateUserController.createUser( req.body.username, req.body.username ).then( res.jsonOutDecide );
});

Based on the accepted answer i was able to put together the following middleware:

    /**
 * unify the json output of any response with jsonFail and jsonSuccess methods
 */
module.exports = function(){
    return function(req, res, next) {

        /**
         * Error json output
         * @param errObj (optional)
         * @returns {*}
         */
        res.jsonFail = function ( errObj ) {
            errObj = errObj || {};
            return res.json({
                success : false,
                error   : errObj
            })
        };

        /**
         * Success json output
         * @param data
         * @returns {*}
         */
        res.jsonSuccess = function ( data ) {
            data = data || {};
            return res.json({
                success: true,
                data: data
            });
        };

        /**
         * Accepts a promise and passes onto the success or fail options above
         * @param p
         * @returns {*}
         */
        res.jsonPromise = function(p) {
            return p.then(res.jsonSuccess, res.jsonFail);
        };

        next();
    };
};

Which can now very easily be used in a routes file which uses a prmoise based controller eg:

router.get('/get-all', function(req, res) {
    res.jsonPromise( CreateUserController.getAllUsers( ) );
});
  • This doesn't really simplify things and not quite what you are looking for but you can do `then(res.jsonGood).catch(res.jsonBad)` – adam-beck Aug 08 '16 at 18:53
  • it might do.. i wonder if there is a higher up config that would allow all promises not caught by a 2nd param function to be one function. This way the routers need only concern themselves with the success and the fails at the router level would be caught by the catch net –  Aug 08 '16 at 18:56
  • 1
    @adam-beck But [that's not the same thing](http://stackoverflow.com/a/24663315/1048572) – Bergi Aug 08 '16 at 18:56
  • @Bergi did not know that. Thanks for sharing! – adam-beck Aug 08 '16 at 19:19

2 Answers2

1

Is it possible to pass one parameter to the then() that could handle both the resolve and reject?

No.

What you want to do instead is to define a function that you can pass the promise to:

function jsonPromise(res, p) {
    return p.then(res.jsonSuccess, res.jsonFail);
}

so that you can do

router.post('/create-user', function(req, res) {
    jsonPromise( res, CreateUserController.createUser( req.body.username, req.body.username ) );
});

Even more elaborate would be to take the whole promise-returning function and decorate it:

function jsonResponse(f) {
    return function(req, res) {
        Promise.resolve(req).then(f).then(res.jsonSuccess, res.jsonFail);
        // similar to `f(req).…`, but catching exceptions and allowing plain return values
    };
}

router.post('/create-user', jsonResponse(function(req) {
    return CreateUserController.createUser( req.body.username, req.body.username );
}));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • oh thats very nice! thank you very much. very nice thinking –  Aug 08 '16 at 19:00
  • This is nice, but I think you run the risk of having hard to read code. This is obviously opinionated but as someone working on this I would have to understand how the `json` function works. – adam-beck Aug 08 '16 at 19:01
  • true, i actually preferred the 1st step here. But instead of placing this in a the router as illustrated above, place this into a middleware that in turn calls the jsonSuccess or fail middleware –  Aug 08 '16 at 19:03
  • @adam-beck: Possibly better than having to understand how both the `res.jsonSuccess` and `res.jsonFail` functions work :-) I'm not so well-versed in express.js, maybe it would be more idiomatic to define that `jsonPromise` function as a method of `res`. – Bergi Aug 08 '16 at 19:03
  • I was more or less trying to state, "just because you can doesn't mean you should". You would still have to know how those 2 functions work but now they are "hidden" from you. I'm not dissing your code or answer, just giving my 2 cents where it probably isn't needed :) – adam-beck Aug 08 '16 at 19:05
  • Depends if you have an ide or not and how well the code is documented :) –  Aug 08 '16 at 19:12
0

No you can't have a single function that will handle both the resolve and the reject. The reason is because if there is an error in the createUser() function the code execution will look for the first place an error will be handled. That is, either in the second argument to a .then() or a .catch()

adam-beck
  • 5,659
  • 5
  • 20
  • 34