Request-Promise
The world-famous HTTP client "Request" now Promises/A+ compliant. Powered by Bluebird.
Bluebird and Request are pretty awesome, but I found myself using the same design pattern. Request-Promise adds a then
method to the Request object which returns a Bluebird promise for chainability. By default, http response codes other than 2xx will cause the promise to be rejected. This can be overwritten by setting options.simple
to false
.
Request-Promise is a drop-in replacement for Request
Since version 0.3.0 Request-Promise is not a wrapper around Request anymore. It now adds a then
method to Request and exports the original Request object. This means you can now use all features of Request.
See the migration instructions for important changes. Issues and pull requests for 0.2.x are still welcome.
Installation
This module is installed via npm:
npm install request-promise
Request-Promise depends on loosely defined versions of Request and Bluebird. If you want to use specific versions of those modules please install them beforehand.
Node.js version 0.10 and up is supported.
Examples
var rp = require('request-promise');
rp('http://www.google.com')
.then(console.dir)
.catch(console.error);
var options = {
uri : 'http://posttestserver.com/post.php',
method : 'POST'
};
rp(options)
.then(console.dir)
.catch(console.error);
options.transform = function (data) { return data.length; };
rp(options)
.then(console.dir)
.catch(console.error);
options = {
method: 'DELETE',
uri: 'http://my-server/path/to/resource/1234',
resolveWithFullResponse: true
};
rp(options)
.then(function (response) {
console.log("DELETE succeeded with status %d", response.statusCode);
})
.catch(console.error);
API in Detail
Consider Request-Promise being:
- A Request object
- With an identical API:
require('request-promise') == require('request')
so to say
- Plus some methods on a request call object:
rp(...).then(...)
or e.g. rp.post(...).then(...)
which turn rp(...)
and rp.post(...)
into promisesrp(...).catch(...)
or e.g. rp.del(...).catch(...)
which is the same method as provided by Bluebird promises
- Plus some additional options:
simple
which is a boolean to set whether status codes other than 2xx should also reject the promiseresolveWithFullResponse
which is a boolean to set whether the promise should be resolve with the full response or just the response bodytransform
which takes a function to transform the response into a custom value with which the promise is resolved
The objects returned by request calls like rp(...)
or e.g. rp.post(...)
are regular Promises/A+ compliant promises and can be assimilated by any compatible promise library. However, the methods .then(...)
and .catch(...)
- which you can call on the request call objects - return a full-fledged Bluebird promise. That means you have the full Bluebird API available for further chaining. E.g.: rp(...).then(...).finally(...)
.then(onFulfilled, onRejected)
var request = require('request');
request('http://google.com', function (err, response, body) {
if (err) {
handleError({ error: err, response: response, ... });
} else if (!(/^2/.test('' + response.statusCode))) {
handleError({ error: body, response: response, ... });
} else {
process(body);
}
});
var rp = require('request-promise');
rp('http://google.com')
.then(process, handleError);
request.post('http://example.com/api', function (err, response, body) { ... });
rp.post('http://example.com/api').then(...);
.catch(onRejected)
rp('http://google.com')
.catch(handleError);
rp('http://google.com')
.then(null, handleError);
rp('http://google.com')
.then(process)
.catch(handleError);
rp('http://google.com')
.then(process, handleError);
Fulfilled promises and the resolveWithFullResponse
option
rp('http://google.com')
.then(function (body) {
});
rp({ uri: 'http://google.com', resolveWithFullResponse: true })
.then(function (response) {
});
Rejected promises and the simple
option
rp('http://google.com')
.catch(function (reason) {
});
var options = { uri: 'http://google.com' };
request(options, function (err, response, body) {
var reason;
if (err) {
reason = {
error: err,
options: options,
response: response
};
} else if (!(/^2/.test('' + response.statusCode))) {
reason = {
error: body,
options: options,
response: response,
statusCode: response.statusCode
};
}
if (reason) {
}
});
rp({ uri: 'http://google.com', simple: false })
.catch(function (reason) {
});
request(options, function (err, response, body) {
if (err) {
var reason = {
error: err,
options: options,
response: response
};
}
});
The transform
function
You can pass a function to options.transform
to generate a custom fulfillment value when the promise gets resolved.
var options = {
uri: 'http://google.com',
transform: function (body, response) {
return body.split('').reverse().join('');
}
};
rp(options)
.then(function (reversedBody) {
});
var $ = require('cheerio');
function autoParse(body, response) {
if (response.headers['content-type'] === 'application/json') {
return JSON.parse(body);
} else if (response.headers['content-type'] === 'text/html') {
return $.load(body);
} else {
return body;
}
}
options.transform = autoParse;
rp(options)
.then(function (autoParsedBody) {
});
var rpap = rp.defaults({ transform: autoParse });
rpap('http://google.com')
.then(function (autoParsedBody) {
});
rpap('http://echojs.com')
.then(function (autoParsedBody) {
});
Debugging
The ways to debug the operation of Request-Promise are the same as described for Request. These are:
- Launch the node process like
NODE_DEBUG=request node script.js
(lib,request,otherlib
works too). - Set
require('request-promise').debug = true
at any time (this does the same thing as #1). - Use the request-debug module to view request and response headers and bodies. Instrument Request-Promise with
require('request-debug')(rp);
.
Migrating from 0.2.x to 0.3.x
The module was rewritten with great care accompanied by plenty of automated tests. In most cases you can just update Request-Promise and your code will continue to work.
First and foremost Request-Promise now exposes Request directly. That means the API sticks to that of Request. The only methods and options introduced by Request-Promise are:
- The
then
method - The
catch
method - The
simple
option - The
resolveWithFullResponse
option - The
transform
option
In regard to these methods and options Request-Promise 0.3.x is largely compatible with 0.2.x. All other parts of the API may differ in favor of the original behavior of Request.
Changes and Removed Quirks
(rp_02x
and rp_03x
refer to the function exported by the respective version of Request-Promise.)
rp_02x(...)
returned a Bluebird promise. rp_03x(...)
returns a Request instance with a then
and a catch
method. If you used any Bluebird method other than then
and catch
as the first method in the chain, your code cannot use Request-Promise 0.3.x right away. Please note that this only applies to the FIRST method in the chain. E.g. rp_03x(...).then(...).finally(...)
is still possible. Only something like rp_03x(...).finally(...)
is not possible. Please open an issue if you need support for e.g. finally
as the first method in the chain.- The options
simple
and resolveWithFullResponse
must be of type boolean. If they are of different type Request-Promise 0.3.x will use the defaults. In 0.2.x the behavior for non-boolean values was different. - The object passed as the reason of a rejected promise contains an
options
object that differs between both versions. Especially the method
property is not set if the default (GET) is applied. - If you called
rp_02x(...)
without appending a .then(...)
call occurring errors may have been discarded. Request-Promise 0.3.x may now throw those. However, if you append a .then(...)
call those errors reject the promise in both versions. rp_03x.head(...)
throws an exception if the options contain a request body. This is due to Requests original implementation which was not used in Request-Promise 0.2.x.rp_02x.request
does not exist in Request-Promise 0.3.x since Request is exported directly. (rp_02x.request === rp_03x
)
Can I trust this module?
The 145 lines of code are covered by 919 lines of test code producing a test coverage of 100% and beyond. Additionally, the original tests of Request were executed on Request-Promise to ensure that we can call it "a drop-in replacement for Request". So yes, we did our best to make Request-Promise live up to the quality Request is known for.
However, there is one important design detail: Request-Promise passes a callback to each Request call which it uses to resolve or reject the promise. The callback is also registered if you don't use the promise features in a certain request. E.g. you may only use streaming: rp(...).pipe(...)
As a result, additional code is executed that buffers the streamed data and passes it as the response body to the "complete" event. If you stream large quantities of data the buffer grows big and that has an impact on your memory footprint. In these cases you can just var request = require('request');
and use request
for streaming large quantities of data.
Contributing
To set up your development environment:
- clone the repo to your desktop,
- in the shell
cd
to the main folder, - hit
npm install
, - hit
npm install gulp -g
if you haven't installed gulp globally yet, and - run
gulp dev
. (Or run node ./node_modules/.bin/gulp dev
if you don't want to install gulp globally.)
gulp dev
watches all source files and if you save some changes it will lint the code and execute all tests. The test coverage report can be viewed from ./coverage/lcov-report/index.html
.
If you want to debug a test you should use gulp test-without-coverage
to run all tests without obscuring the code by the test coverage instrumentation.
Change History
Main Branch
Version 0.2.x Branch
- v0.2.6 (2014-11-09)
- When calling
rp.defaults(...)
the passed resolveWithFullResponse
option is not always overwritten by the default anymore. - The function passed as the
transform
option now also gets the full response as the second parameter. The new signature is: function (body, response) { }
- If the transform function throws an exception it is caught and the promise is rejected with it.
- v0.2.5 (2014-11-06)
- The Request instance which is wrapped by Request-Promise is exposed as
rp.request
.
MIT Licensed
See the LICENSE file for details.