32

Bluebird promisifaction is a little magic, and request is quite a mess (it's a function which behaves as an object with methods).

The specific scenario is quite simple: I have a request instance with cookies enabled, via a cookie jar (not using request's global cookie handler). How can I effectively promisify it, and all of the methods it supports?

Ideally, I'd like to be able to:

  • call request(url) -> Promise
  • call request.getAsync(url) -> Promise
  • call request.postAsync(url, {}) -> Promise

It seems as though Promise.promisifyAll(request) is ineffective (as I'm getting "postAsync is not defined").

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • Full disclosure, I'm the author: [http-as-promised](https://www.npmjs.com/package/http-as-promised). – idbehold Feb 03 '15 at 20:55
  • 8
    @idbehold you only have to disclose things if you're going to say something about them :P – Benjamin Gruenbaum Feb 03 '15 at 21:04
  • This isn't trivial, please consider opening an issue at the issue tracker. – Benjamin Gruenbaum Feb 03 '15 at 21:24
  • Isn't `request(...)` same as `request.get(...)`? Why do you need both? – Esailija Feb 04 '15 at 08:11
  • @Esailija I guess it is, but it's mainly to avoid confusion. I guess the point is that all of the API is promisified, and I don't have a callback left hanging because I didn't realize I need it. Strictly speaking, yes. One of the two is optional. – Madara's Ghost Feb 04 '15 at 08:18
  • @SecondRikudo if your module is just going to make request its API directly what is the point of using your module? I thought you are only using request internally and export your own wrapper that fixes these issues with request promisification. – Esailija Feb 04 '15 at 08:30
  • @Esailija that's exactly the case. Right now I have a half-assed solution of using `Promise.fromNode()` to directly promisify the functions I need, and delegate the facade container methods to them. It's ugly, but it works. However, what makes `request` so special that `promisifyAll` has so much trouble with it? – Madara's Ghost Feb 04 '15 at 08:35
  • @SecondRikudo when you `promisifyAll` it adds the methods on the request object (which is a function) this works just fine when you call the methods. However, the `request` function's function code is still normal, to promisify that you need to call normal `promisify` but it returns a new function object that doesn't copy properties over. Maybe if it copied custom properties over it would work, cannot test right now. – Esailija Feb 04 '15 at 08:37
  • @SecondRikudo my point is that you can just `promisifyAll` and then in your exported function just delegate to `request.getAsync` and just don't use `request` module as a function. I recommend clearing the module cache so that when someone requires `"request"` somewhere else, it wouldn't have the Async methods on it. – Esailija Feb 04 '15 at 08:41
  • @Esailija That might indeed work. But it still smells like a limitation of either library. – Madara's Ghost Feb 04 '15 at 08:56
  • @SecondRikudo yes but I can't see why copying couldn't work so it will be likely fixed in a release tonight – Esailija Feb 04 '15 at 09:00

4 Answers4

37

The following should work:

var request = Promise.promisify(require("request"));
Promise.promisifyAll(request);

Note that this means that request is not a free function since promisification works with prototype methods since the this isn't known in advance. It will only work in newer versions of bluebird. Repeat it when you need to when forking the request object for cookies.


If you're using Bluebird v3, you'll want to use the multiArgs option:

var request = Promise.promisify(require("request"), {multiArgs: true});
Promise.promisifyAll(request, {multiArgs: true})

This is because the callback for request is (err, response, body): the default behavior of Bluebird v3 is to only take the first success value argument (i.e. response) and to ignore the others (i.e. body).

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 1
    How does that work with `request`'s cookies? Because as far as I can tell, it creates a new instance of `request` with a cookie jar in it – Madara's Ghost Feb 03 '15 at 21:00
  • @SecondRikudo it now works in new bluebird. Please check it out :) – Benjamin Gruenbaum Feb 26 '15 at 00:08
  • 1
    when you say "newer versions", which versions specifically? – dopatraman Jun 17 '15 at 17:58
  • 1
    What does Bluebird's promisification do with the `response` and `body` arguments sent to the `request.get()` callback? Is it ignoring the body and only using the `response` argument as the resolved value of the promise? – jfriend00 Dec 14 '16 at 19:52
  • @Retsam that's just `{multiArgs: true}`, it's part of the answer. – Benjamin Gruenbaum Mar 11 '17 at 11:59
  • @BenjaminGruenbaum Yeah, I edited that into the answer after making my comment and looking into it more. (I should edit or delete my comment, I guess) – Retsam Mar 13 '17 at 06:15
  • @BenjaminGruenbaum could you expand on the concept of a "free function" and its promisification? I don't see many examples of this particular concept anywhere else. Thanks. – nweiler Sep 05 '18 at 20:59
33

you can use the request-promise module.

The world-famous HTTP client "Request" now Promises/A+ compliant. Powered by Bluebird.

Install the module and you can use request in promise style.

npm install request-promise
Arvind Sridharan
  • 3,885
  • 4
  • 29
  • 54
11

Note that you don't need the third callback parameter, body. It is also present on the response parameter. If you check the source you can see that body is just a convenience for response.body. They are guaranteed to always be the same.

This means that simple promisification as described in other answers on this page is enough to get all response data.

const request = require('request')
const { promisify } = require('util')
const rp = promisify(request)

rp('https://example.com').then(({body, statusCode}) => ...)

This is only true of the response passed to the callback/promise. The response object passed to the response event is a standard http.IncomingMessage and as such has no body property.

Tamlyn
  • 22,122
  • 12
  • 111
  • 127
9

I give an example, by util base on Node.js v11.10.0

import { get, post } from "request";
import { promisify } from "util";

const [getAsync, postAsync] = [get, post].map(promisify);


getAsync("http://stackoverflow.com")
    .then(({statusCode, body}) => { 
       //do something 
     });

Or, equivalently using async/await:

const foo = async () => {
    const {statusCode, body} = await getAsync("http://stackoverflow.com")
    // do something
}
Little Roys
  • 5,383
  • 3
  • 30
  • 28
  • Note that the original question is about the `request` object specifically, not any of its methods, those are (relatively) easy. – Madara's Ghost Feb 27 '19 at 10:13