I've been working on a small node.js project lately to promise-ify request. I'm calling it request-as-promise. You can install it via npm with npm i --save request-as-promise.

Why

The short version is that I find promises to be the best mechanism for handling asynchronicity. Request-promise is the largest existing project to do this (that I know of), and it seems to be perfectly competent. But I have some issues with it anyway. In no particular order: it relies on Bluebird; it adds considerable complication to the already complicated request module; the API is remarkably unstable.

I spend a lot of time working with Angular and Protractor. These both use the Q.js promise library (more-or-less). While I could pick on these projects for other things, Q isn't one of them. It's the most mature, well tested, and full featured promise library in the JS ecosystem. And it's the one I default to. So bluebird would be a redundant additional dependency for me, which I'd rather avoid.

Besides, I've made several partial implementations of this basic idea in various projects. For network I/O the only patterns that make sense to me are streams or promises. Managing network I/O with callbacks just seems like madness. And so this project can now be a well tested drop in replacement for several haphazard one-off implementations that I have lying around.

How it works

My goal here was to replicate the majority of the interface to request, maintaining the style and syntax as nearly as possible. For the most part, that is a pretty straightforward task. For instance, request will accept a URL string as an argument, or a complex options object. Processing that input adds some work, but isn't actually complicated. Likewise, supporting all the http verb shortcut methods (request.get, request.post, etc.) is tedious but not difficult. In this case I opted to simply define these functions directly. They set the appropriate method on the options object and then call the main requestAsPromised function. This is in contrast to the path that request itself takes, where an array of verb strings is parsed to create and add the appropriate functions to the main request object.

request.defaults was another matter. My eventual solution was still quite simple (which is to say not complex). In this project, requestAsPromised.defaults is a kind of light-weight factory. It uses Function.bind to create a unique instance of the main function where the default options are set as a propery on that function's this object. Object.assign is used to merge the default this.opts object with the given options argument, and then the result is passed to request as normal.

Usage

There are several existing projects I'd like to update to make use of this module. And I'll likely use it for some new projects as well where I need to use request for some reason.

To quote from the readme:

npm i --save request-as-promise
var reqAP = require('request-as-promise');

reqAP('example.com')
    .spread(function success(response, data) {
       // Do stuff with the response;
    });

But, in doing the research for this project, I came upon the Got module. Got exists because request is a mess. That's something I've known for a while, but I didn't know there was a reasonable alternative. There is though, and it's Got. It does everything I would want, and the API seems to be a whole lot cleaner. I expect that for any future project where I get to choose, I'll choose got over request every time.