Search code examples
javascriptexpressbluebirdpg-promise

Pg-promise: chaining conditional queries


I'm trying to find the correct way of chaining conditional queries.

Here is some pseudo-code to illustrate my situation:

check whether the an item exists;
  if no:
    reply with status 404;
  if yes:
    check whether the user owns the item;
      if no:
        redirect to another page;
      if yes:
        retrieve the information about the item and render the page;

My first intuition would be to use tasks in order to reuse the same connection but because of the different possible outcomes, I am having a hard time figuring out how to properly deal with the promises:

db.task(t => {
  return t.items.exists(itemId)
    .then(exists => {
      if (!exists) { // item does not exist
        // 404
      }

      return t.items.isOwner(itemId, userId)
        .then(isOwner => {
          if (!isOwner) {
            // redirect to some other page
          }

          return t.items.getById(itemId);
        })
    })
})
.then(data => {
  // OK, process data and render
})
.catch(console.error); // unexpected errors

If I try to redirect to a 404 page for instance, the promise will still be resolved afterwards. An another way would be to have the following:

if (!exists) { // item does not exist
  return Promise.reject('404');
}

...

.then(data => {
  // OK, process data and render
}, reason => {
  // KO, conditions were not met to resolve
})

which 'works', but catches both errors and unmet conditions. I would prefer to have a dedicated 'unmet condition' handler.

Yet another approach I thought of:

var p = db.task(t => {
  return t.items.exists(itemId)
    .then(exists => {
      if (!exists) { // item does not exist
        //  resolve p (and break promise chain) with something like
        //  p.resolve(() => {
        //    return res.redirect...
        //  });
      }

      // else we can go on with the queries
      return t.items.isOwner(itemId, userId);
    }
    .then(isOwner => {
      if (!isOwner) {
        // resolve p and redirect to some other page
      }

      return t.items.getById(itemId);
    })
    .then(item => {
      // everything OK, resolve with a handler to render the page
    });
})
.then(action => {
  action();
})
.catch(console.error); // unexpected errors

But I don't see any way I can resolve p. Calling Promise.resolve(...) inside a nested promise resolves the next promise itself before falling through p's then.

What is the recommended way of chaining conditional queries and dealing with different results in pg-promise while keeping an eye on performance?


Solution

  • The issue that the author has is mainly with using promises, and not so much with pg-promise.

    db.task('easy-peasy', async t => {
        if (await t.items.exists(itemId)) {
            // reply with status 404;
        } else {
            if (await t.items.isOwner(itemId, userId)) {
                // redirect to some other page
            } else {
                return t.items.getById(itemId); // return the data
            }
        }
    })
        .then(data => {
            if (data) {
                // Task returned data;
                // Render the data;
            }
        })
        .catch(console.error); // unexpected errors