63

In my program I make async call for my function from another API module:

var info = await api.MyRequest(value);

Module code:

var request = require("request")

module.exports.MyRequest = async function MyRequest(value) {
    var options = {
        uri: "http://some_url",
        method: "GET",
        qs: {  // Query string like ?key=value&...
            key : value
        },
        json: true
    }

    try {
        var result = await request(options);
        return result;
    } catch (err) {
        console.error(err);
    }
}

Execution returns immediately, however result and therefore info contains request object and request body - info.body like key=value&..., not required response body.

What I'm doing wrong? How to fix? What is proper request usage with async, or it only works correctly with promises like mentioned here: Why await is not working for node request module? Following article mentioned it is possible: Mastering Async Await in Node.js.

2
  • async and await work in nodejs 7 latest build. so please make sure you have latest build of nodejs 7. For more infomation you can refer this link blog.risingstack.com/async-await-node-js-7-nightly Commented Aug 20, 2017 at 5:06
  • @Aabid I have Node.js 8.4 Commented Aug 20, 2017 at 8:16

8 Answers 8

82

You need to use the request-promise module, not the request module or http.request().

await works on functions that return a promise, not on functions that return the request object and expect you to use callbacks or event listeners to know when things are done.

The request-promise module supports the same features as the request module, but asynchronous functions in it return promises so you can use either .then() or await with them rather than the callbacks that the request module expects.

So, install the request-promise module and then change this:

var request = require("request");

to this:

const request = require("request-promise");

Then, you can do:

var result = await request(options);

EDIT Jan, 2020 - request() module in maintenance mode

FYI, the request module and its derivatives like request-promise are now in maintenance mode and will not be actively developed to add new features. You can read more about the reasoning here. There is a list of alternatives in this table with some discussion of each one.

I have been using got() myself and it's built from the beginning to use promises, supports many of the same options as the request() module and is simple to program.

Newer versions of nodejs also have the fetch() interface built-in directly for making http requests and getting responses back (same API as fetch in the browser, but built into nodejs).

Sign up to request clarification or add additional context in comments.

4 Comments

Why are you not saying that one can simply wrap nodejs' http methods in a promise like return new Promise(function (resolve, reject) ?
@RonnieRoyston - You can wrap anything you like - this answer at the time in 2017 offers the simplest way to add promise support to the request module (to just add 8 characters to one line of code to add the implementation that already fully wrapped it). These days in 2023, it's kind of silly to write your own promise wrappers for making http requests. We have fetch(), got(), axios(), etc... that all do this for you already and all have ground up implementations that use promises. And, the request module is deprecated so nobody should be writing new code with it any more.
Simply use Nodejs util.promisify on the http.request method?
@RonnieRoyston - Why bother in 2023? If you want promises, pick a library that natively supports them. Also, http.request() is a LOT lower level than any of these other libraries. The reason people used the request module in the first place is because it does all sorts of things that http.request() doesn't do. http.request() doesn't read the response for you. It just sends the request and gets you a response stream and you have to then read the stream yourself. And, http.request() doesn't use the nodejs asynchronous calling convention that util.promisify() expects either.
33

Pretty sure you can also do the following. If what you need does not return Promise by default you can provide it via new Promise method. Above answer is less verbose though.

async function getBody(url) {
  const options = {
    url: url,
    method: 'GET',
  };

  // Return new promise
  return new Promise(function(resolve, reject) {
    // Do async job
    request.get(options, function(err, resp, body) {
      if (err) {
        reject(err);
      } else {
        resolve(body);
      }
    })
  })
}

5 Comments

What is the purpose of async here, when it also work fine without the async keyword? Pardon me with these question.
You can do this, and it will work, but it becomes very difficult to unit test function getBody(url). I just learned the hard way!
as for me this is the best answer
Thanks @user3520261 your answer solve my big issue.
The async keyword is completely optional here and in fact redundant as the function is already returning a promise
13

I just managed to get it to work with async/await. I wrapped it inside a function promisifiedRequest to return a promise that runs the request callback and resolves or rejects it based on error and response.

const request = require('request');

const promisifiedRequest = function(options) {
  return new Promise((resolve,reject) => {
    request(options, (error, response, body) => {
      if (response) {
        return resolve(response);
      }
      if (error) {
        return reject(error);
      }
    });
  });
};

(async function() {
  const options = {
    url: 'https://www.google.com',
    method: 'GET',
    gzip: true,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36'
    }
  };

  let response = await promisifiedRequest(options);

  console.log(response.headers);
  console.log(response.body);
})();

Comments

12

Since request-promise has been deprecated, here are other options that don't depend on the NPM request package. got has been mentioned already, but it depends on 11 other packages. axios, in contrast, only has 1 dependency (for redirects). Everything else is natively implemented and built on top of the native NodeJS packages.

Here is the same example using axios:

const axios = require('axios')
const response = await axios.get(url)
const result = response.data

or, as a one-liner in JavaScript

const result = (await axios.get(url)).data

One-liner in TypeScript:

const {data} = await axios.get(url)

Comments

7

For simple cases where you don't need advanced features like cookies, following redirects and retrying, you can use native http/https module to make requests:

const https = require('https')

async function fetch(url) {
  return new Promise((resolve, reject) => {
    const request = https.get(url, { timeout: 1000 }, (res) => {
      if (res.statusCode < 200 || res.statusCode > 299) {
        return reject(new Error(`HTTP status code ${res.statusCode}`))
      }

      const body = []
      res.on('data', (chunk) => body.push(chunk))
      res.on('end', () => {
        const resString = Buffer.concat(body).toString()
        resolve(resString)
      })
    })

    request.on('error', (err) => {
      reject(err)
    })
    request.on('timeout', () => {
      request.destroy()
      reject(new Error('timed out'))
    })
  })
}

const res = await fetch('https://...')

Comments

1

7 months later im here to let you know... The Request Method Has A Callback As The Second Parameter

The Data you want to return can be attached to the original request body JSON then accessed in the next() function.

async function incomingRequest(req, res, next) {
 
async function pR_callback(error, response, body) {
    if (!error) {
        var data = JSON.parse(body);
        p_response = data
        console.log('after_request', p_response);
        req.body.confirmation = p_response
        next()

        return data

    } else {
        console.log("rejected")
        const data = 'error'
        return data
    }
}

//-----------------------------------------
var p = await request(out, pR_callback)

}

Then the express route

app.all('/this_route', incomingRequest, async (req, res, next) => {
    res.send({pass: "true", confirmation: req.body.confirmation})
})

Comments

0

For anyone using NodeJS only, you have to promisify the http/https .request methods. Probably cleanest to create a module that does this then import it for use like:

req.mjs

import {request as httpSync} from 'node:http';
import {request as httpsSync} from 'node:https';

function http(options,body){
  return new Promise(function (resolve, reject) {
    const req = httpSync(options, (res) => {
      var data = '';
      res.setEncoding('utf8');
      res.on('data', (chunk) => { data += chunk });
      res.on('end', () => {
        resolve({"body":data,"statusCode":res.statusCode,"headers":res.headers})
      });
    });

    req.on('error', (e) => {
      reject(e);
      console.error(`problem with request: ${e.message}`);
    });
    if(!isEmpty(body))(req.write(body));
    req.end();
  });
}

function https(options,body){
  return new Promise(function (resolve, reject) {
    const req = httpsSync(options, (res) => {
      var data = '';
      res.setEncoding('utf8');
      res.on('data', (chunk) => { data += chunk });
      res.on('end', () => {
        resolve({"body":data,"statusCode":res.statusCode,"headers":res.headers})
      });
    });
    req.on('error', (e) => {
      reject(e);
    });
    if(!isEmpty(body))(req.write(body));
    req.end();
  });
}

function isEmpty(obj) {
  for (var i in obj) return false;
  return true;
}

export default {http,https};

myScript.mjs

import req from './req.mjs';


  try {
    let options = {
      hostname: 'www.cisco.com',
      port: 443,
      method: 'GET'
    };
    let res = await req.https(options);
    console.log(res.body,res.statusCode,JSON.stringify(res.headers);
  } catch(e) {
    console.log(e);
  }

Comments

0

Since mdoes request libraries are in "maintenance mode", and the http and https libraries do not appear to have any async wrapper, here is an updated async way to use nodes built in https library in an async fashion.

export async function fetchHTML(query) {
    //console.log(`\n function fetchHTML(\n${query}) { `)
    function doRequest() {
        return new Promise((resolve, reject) => {
            //console.log('query ', query)
            https.get(query, (res) => {
                //console.log('statusCode:', res.statusCode);
                //console.log('headers:', res.headers);
                res.on('data', (d) => {
                    resolve(res);
                });
            }).on('error', (e) => {
                reject(e);
            });
        });
    }
    let res = await doRequest()
    //do something with response here
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.