request-promise

Promises/A+ logo

Gitter
Build Status
Coverage Status
Dependency Status
Known Vulnerabilities

As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time. This package is also deprecated because it depends on request.

Fyi, here is the reasoning of request‘s deprecation and a list of alternative libraries.

The simplified HTTP request client ‘request’ with Promise support. Powered by Bluebird.

Request and Bluebird are pretty awesome, but I found myself using the same design pattern. Request-Promise adds a Bluebird-powered .then(...) method to Request call objects. By default, http response codes other than 2xx will cause the promise to be rejected. This can be overwritten by setting options.simple = false.

Also check out the new libraries that are very similar to request-promise v4:

  • request-promise-native v1 – Does not depend on Bluebird and uses native ES6 promises instead.
  • request-promise-any v1 – Allows you to register any Promise library supported by any-promise.

Migration from v3 to v4

  1. request became a peer dependency. Thus make sure that request is installed into your project as a direct dependency. (npm install --save request)
  2. Continuation Local Storage is no longer supported. However, you can get back the support by using request-promise-any.
  3. When you migrated your transform function to v3 and had to add if (!(/^2/.test('' + response.statusCode))) { return resolveWithFullResponse ? response : body; } you may now set the option transform2xxOnly = true instead.

Migration from v2 to v3

  1. The handling of the transform function got overhauled. This has two effects:

    • StatusCodeError.response is the transformed instead of the original response now. This error is thrown for non-2xx responses when options.simple is true (default). Please update your transform functions to also cover the transformation of non-2xx responses. To get the old behavior you may add if (!(/^2/.test('' + response.statusCode))) { return resolveWithFullResponse ? response : body; } to the first line of your transform functions that are used for requests with options.simple === true. However, you may prefer updating your transform functions to being able to transform 2xx as well as non-2xx responses because this decouples their implementation from the use of the simple option when doing requests.
    • If a transform operation throws an error, the request will be rejected with a TransformError. Its cause attribute contains the error thrown by the transform operation. Previously, the request was rejected directly with the error thrown by the transform operation. Wrapping it into a TransformError makes the error handling easier.
  2. Bluebird got updated from v2 to v3. This won’t make a difference for most use cases. However, if you use advanced Promise chains starting with the Promise returned by Request-Promise, please check Bluebird’s new features and changes.

Installation

This module is installed via npm:

npm install --save requestnpm install --save request-promise

request is defined as a peer-dependency and thus has to be installed separately.

Cheat Sheet

var

 rp 

=

 

require

(

'

request-promise

'

)

;

Since request-promise wraps around request everything that works with request also works with request-promise. Also check out the request docs for more examples.

Crawl a webpage

rp

(

'

http://www.google.com

'

)

    

.

then

(

function

 

(

htmlString

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

Crawl a webpage better

var

 cheerio 

=

 

require

(

'

cheerio

'

)

;

 

 

var

 options 

=

 

{

    uri

:

 

'

http://www.google.com

'

,

    

transform

:

 

function

 

(

body

)

 

{

        

return

 

cheerio

.

load

(

body

)

;

    

}

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

$

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

GET something from a JSON REST API

var

 options 

=

 

{

    uri

:

 

'

https://api.github.com/user/repos

'

,

    qs

:

 

{

        access_token

:

 

'

xxxxx xxxxx

'

 

    

}

,

    headers

:

 

{

        

'

User-Agent

'

:

 

'

Request-Promise

'

    

}

,

    json

:

 

true

 

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

repos

)

 

{

        

console

.

log

(

'

User has %d repos

'

,

 

repos

.

length

)

;

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

POST data to a JSON REST API

Set option.body to your data and json: true to encode the body as JSON. See below for HTML forms.

var

 options 

=

 

{

    method

:

 

'

POST

'

,

    uri

:

 

'

http://api.posttestserver.com/post

'

,

    body

:

 

{

        some

:

 

'

payload

'

    

}

,

    json

:

 

true

 

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

parsedBody

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

POST like HTML forms do

Pass your data to options.form to encode the body the same way as HTML forms do:

var

 options 

=

 

{

    method

:

 

'

POST

'

,

    uri

:

 

'

http://posttestserver.com/post.php

'

,

    form

:

 

{

        

        name

:

 

'

Josh

'

    

}

,

    headers

:

 

{

        

 

    

}

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

body

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

If you want to include a file upload then use options.formData:

var

 options 

=

 

{

    method

:

 

'

POST

'

,

    uri

:

 

'

http://posttestserver.com/post.php

'

,

    formData

:

 

{

        

        name

:

 

'

Jenn

'

,

        

        file

:

 

{

            value

:

 

fs

.

createReadStream

(

'

test/test.jpg

'

)

,

            options

:

 

{

                filename

:

 

'

test.jpg

'

,

                contentType

:

 

'

image/jpg

'

            

}

        

}

    

}

,

    headers

:

 

{

        

 

    

}

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

body

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

Include a cookie

var

 tough 

=

 

require

(

'

tough-cookie

'

)

;

 

let

 cookie 

=

 

new

 

tough

.

Cookie

(

{

    key

:

 

"

some_key

"

,

    value

:

 

"

some_value

"

,

    domain

:

 

'

api.mydomain.com

'

,

    httpOnly

:

 

true

,

    maxAge

:

 

31536000

}

)

;

 

var

 cookiejar 

=

 

rp

.

jar

(

)

;

cookiejar

.

setCookie

(

cookie

,

 

'

https://api.mydomain.com

'

)

;

 

var

 options 

=

 

{

    uri

:

 

'

https://api.mydomain.com/

...

'

,

    jar

:

 cookiejar 

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

body

)

 

{

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

Get the full response instead of just the body

var

 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

(

function

 

(

err

)

 

{

        

    

}

)

;

Get a rejection only if the request failed for technical reasons

var

 options 

=

 

{

    uri

:

 

'

http://www.google.com/this-page-does-not-exist.html

'

,

    simple

:

 

false

    

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

body

)

 

{

        

        

    

}

)

    

.

catch

(

function

 

(

err

)

 

{

        

    

}

)

;

For more options checkout the Request docs.

API in Detail

Consider Request-Promise being:

  • A Request object
    • With an identical API: require('request-promise') == require('request') so to say
    • However, STREAMING THE RESPONSE (e.g. .pipe(...)) is DISCOURAGED because Request-Promise would grow the memory footprint for large requests unnecessarily high. Use the original Request library for that. You can use both libraries in the same project.
  • Plus some methods on a request call object:
    • rp(...).then(...) or e.g. rp.post(...).then(...) which turn rp(...) and rp.post(...) into promises
    • rp(...).catch(...) or e.g. rp.del(...).catch(...) which is the same method as provided by Bluebird promises
      • Errors that the request library would pass to the callback are wrapped by request-promise and then passed to the catch handler. See code example below.
    • rp(...).finally(...) or e.g. rp.put(...).finally(...) which is the same method as provided by Bluebird promises
    • rp(...).cancel() or e.g. rp.get(...).cancel() which cancels the request
    • rp(...).promise() or e.g. rp.head(...).promise() which returns the underlying promise so you can access the full Bluebird API
  • Plus some additional options:
    • simple = true which is a boolean to set whether status codes other than 2xx should also reject the promise
    • resolveWithFullResponse = false which is a boolean to set whether the promise should be resolved with the full response or just the response body
    • transform which takes a function to transform the response into a custom value with which the promise is resolved
    • transform2xxOnly = false which is a boolean to set whether the transform function is applied to all responses or only to those with a 2xx status code

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.

The methods .then(...), .catch(...), and .finally(...) – 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(...).spread(...) If, however, you need a method other than .then(...), .catch(...), or .finally(...) to be FIRST in the chain, use .promise(): rp(...).promise().bind(...).then(...)

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

(

...

)

;

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

)

;

For more info on .then(process).catch(handleError) versus .then(process, handleError), see Bluebird docs on promise anti-patterns.

rp

(

'

http://google.com

'

)

    

.

finally

(

function

 

(

)

 

{

        

    

}

)

;

This method cancels the request using Bluebird’s cancellation feature.

When .cancel() is called:

  • the promise will neither be resolved nor rejected and
  • the request is aborted.

.promise() – For advanced use cases

In order to not pollute the Request call objects with the methods of the underlying Bluebird promise, only .then(...), .catch(...), and .finally(...) were exposed to cover most use cases. The effect is that any methods of a Bluebird promise other than .then(...), .catch(...), or .finally(...) cannot be used as the FIRST method in the promise chain:

rp

(

'

http://google.com

'

)

.

then

(

function

 

(

)

 

{

 

...

 

}

)

;

rp

(

'

http://google.com

'

)

.

catch

(

function

 

(

)

 

{

 

...

 

}

)

;

 

rp

(

'

http://google.com

'

)

.

then

(

function

 

(

)

 

{

 

...

 

}

)

.

spread

(

function

 

(

)

 

{

 

...

 

}

)

;

rp

(

'

http://google.com

'

)

.

catch

(

function

 

(

)

 

{

 

...

 

}

)

.

error

(

function

 

(

)

 

{

 

...

 

}

)

;

 

 

rp

(

'

http://google.com

'

)

.

promise

(

)

.

bind

(

this

)

.

then

(

function

 

(

)

 

{

 

...

 

}

)

;

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 

=

 

{

            cause

:

 err

,

            error

:

 err

,

            options

:

 options

,

            response

:

 response

        

}

;

    

}

 

else

 

if

 

(

!

(

/

^

2

/

.

test

(

'

'

 

+

 

response

.

statusCode

)

)

)

 

{

 

        reason 

=

 

{

            statusCode

:

 

response

.

statusCode

,

            error

:

 body

,

            options

:

 options

,

            response

:

 response

        

}

;

    

}

 

    

if

 

(

reason

)

 

{

        

    

}

}

)

;

 

 

rp

(

{

 uri

:

 

'

http://google.com

'

,

 simple

:

 

false

 

}

)

    

.

catch

(

function

 

(

reason

)

 

{

        

    

}

)

;

 

request

(

options

,

 

function

 

(

err

,

 

response

,

 

body

)

 

{

    

if

 

(

err

)

 

{

        

var

 reason 

=

 

{

            cause

:

 err

,

            error

:

 err

,

            options

:

 options

,

            response

:

 response

        

}

;

        

    

}

}

)

;

With version 0.4 the reason objects became Error objects with identical properties to ensure backwards compatibility. These new Error types allow targeted catch blocks:

var

 errors 

=

 

require

(

'

request-promise/errors

'

)

;

 

rp

(

'

http://google.com

'

)

    

.

catch

(

errors

.

StatusCodeError

,

 

function

 

(

reason

)

 

{

        

        

    

}

)

    

.

catch

(

errors

.

RequestError

,

 

function

 

(

reason

)

 

{

        

        

    

}

)

;

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

,

 

resolveWithFullResponse

)

 

{

        

return

 

body

.

split

(

'

'

)

.

reverse

(

)

.

join

(

'

'

)

;

    

}

}

;

 

rp

(

options

)

    

.

then

(

function

 

(

reversedBody

)

 

{

        

    

}

)

;

 

 

var

 $ 

=

 

require

(

'

cheerio

'

)

;

 

 

function

 

autoParse

(

body

,

 

response

,

 

resolveWithFullResponse

)

 

{

    

    

    

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

)

 

{

        

    

}

)

;

The third resolveWithFullResponse parameter of the transform function is equivalent to the option passed with the request. This allows to distinguish whether just the transformed body or the whole response shall be returned by the transform function:

function

 

reverseBody

(

body

,

 

response

,

 

resolveWithFullResponse

)

 

{

    

response

.

body

 

=

 

response

.

body

.

split

(

'

'

)

.

reverse

(

)

.

join

(

'

'

)

;

    

return

 resolveWithFullResponse 

?

 

response

 

:

 

response

.

body

;

}

As of Request-Promise v3 the transform function is ALWAYS executed for non-2xx responses. When options.simple is set to true (default) then non-2xx responses are rejected with a StatusCodeError. In this case the error contains the transformed response:

var

 options 

=

 

{

    uri

:

 

'

http://the-server.com/will-return/404

'

,

    simple

:

 

true

,

    

transform

:

 

function

 

(

body

,

 

response

,

 

resolveWithFullResponse

)

 

{

 

 

}

}

;

 

rp

(

options

)

    

.

catch

(

errors

.

StatusCodeError

,

 

function

 

(

reason

)

 

{

        

    

}

)

;

You may set options.transform2xxOnly = true to only execute the transform function for responses with a 2xx status code. For other status codes – independent of any other settings, e.g. options.simple – the transform function is not executed.

Error handling

If the transform operation fails (throws an error) the request will be rejected with a TransformError:

var

 errors 

=

 

require

(

'

request-promise/errors

'

)

;

 

var

 options 

=

 

{

    uri

:

 

'

http://google.com

'

,

    

transform

:

 

function

 

(

body

,

 

response

,

 

resolveWithFullResponse

)

 

{

        

throw

 

new

 

Error

(

'

Transform failed!

'

)

;

    

}

}

;

 

rp

(

options

)

    

.

catch

(

errors

.

TransformError

,

 

function

 

(

reason

)

 

{

        

console

.

log

(

reason

.

cause

.

message

)

;

 

        

    

}

)

;

Experimental Support for Continuation Local Storage

Continuation Local Storage is no longer supported. However, you can get back the support by using request-promise-any.

Debugging

The ways to debug the operation of Request-Promise are the same as described for Request. These are:

  1. Launch the node process like NODE_DEBUG=request node script.js (lib,request,otherlib works too).
  2. Set require('request-promise').debug = true at any time (this does the same thing as #1).
  3. Use the request-debug module to view request and response headers and bodies. Instrument Request-Promise with require('request-debug')(rp);.

Mocking Request-Promise

Usually you want to mock the whole request function which is returned by require('request-promise'). This is not possible by using a mocking library like sinon.js alone. What you need is a library that ties into the module loader and makes sure that your mock is returned whenever the tested code is calling require('request-promise'). Mockery is one of such libraries.

@florianschmidt1994 kindly shared his solution:

before

(

function

 

(

done

)

 

{

 

    

var

 filename 

=

 

"

fileForResponse

"

;

    

mockery

.

enable

(

{

        warnOnReplace

:

 

false

,

        warnOnUnregistered

:

 

false

,

        useCleanCache

:

 

true

    

}

)

;

 

    

mockery

.

registerMock

(

'

request-promise

'

,

 

function

 

(

)

 

{

        

var

 response 

=

 

fs

.

readFileSync

(

__dirname

 

+

 

'

/data/

'

 

+

 filename

,

 

'

utf8

'

)

;

        

return

 

Bluebird

.

resolve

(

response

.

trim

(

)

)

;

    

}

)

;

 

    

done

(

)

;

}

)

;

 

after

(

function

 

(

done

)

 

{

    

mockery

.

disable

(

)

;

    

mockery

.

deregisterAll

(

)

;

    

done

(

)

;

}

)

;

 

describe

(

'

custom test case

'

,

 

function

 

(

)

 

{

    

    

    

var

 rp 

=

 

require

(

'

request-promise

'

)

;

    

rp

(

...

)

.

then

(

function

(

data

)

 

{

        

    

}

)

;

}

)

;

Based on that you may now build a more sophisticated mock. Sinon.js may be of help as well.

Contributing

To set up your development environment:

  1. clone the repo to your desktop,
  2. in the shell cd to the main folder,
  3. hit npm install,
  4. hit npm install gulp -g if you haven’t installed gulp globally yet, and
  5. 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

License (ISC)

In case you never heard about the ISC license it is functionally equivalent to the MIT license.

See the LICENSE file for details.

Xổ số miền Bắc