LRK9
LRK9

Reputation: 351

In NodeJS, how do I wrap HTTP Requests into Promises?

NodeJS 6.9.3

I wrote a simple NodeJS app that makes a request that looks like this:

var http_request = require('request')


        http_request(
            { method: 'GET'
              , uri: 'https://search-xxxxxx.us-east-1.es.amazonaws.com/facts/_search?pretty=true'
              ,   'content-type': 'application/json'
              ,  body: es_regexp_query_as_string
            }
            , function (error, response, body) {
                if(
                    response.statusCode == 201
                        ||
                        response.statusCode == 200) {

And it works great. I do whatever I need to do in the callback. However, now I need to re-architect this app, so that instead of doing one HTTP request, it can do an arbitrary number of requests. As I understand it, the ideal "design pattern" for handling this in NodeJS is to wrap each request in a Promise, and then hand all the Promises to a call to:

Promise.all(something)

But I've been reading on this, and I can not find out how to transform the HTTP requests into Promises. Or rather, there is a ton of conflicting advice. Apparently the concept of "Promise" has changed a lot in NodeJS, just over the last year or two.

I tried to simply wrap the whole code block in a Promise, but that did not work. So what do I do?

Upvotes: 0

Views: 4273

Answers (2)

If you want a solution without any external module, check this code:

// Dependencies
var http = require('http');
var https = require('https');

var httpRequest = function(options){
  if(!options)      return new Promise((_, reject)=>{ reject( new Error('Missing \'options\' arg.'))})
  if(!options.host) return new Promise((_, reject)=>{ reject( new Error('\'host\' parameter is empty.'))})

  var protocol = (options.protocol || 'http').toLowerCase();
  var method   = (options.method || 'GET').toUpperCase();
  var path     = options.path || '/';
  var port     = options.port || (protocol == 'https' ? 443 : 80);


  var _http = protocol == 'https'? https : http;

  var prom = new Promise((resolve, reject)=>{
    var ops = {
      host : options.host, // here only the domain name
      port : port,
      path : (/^(\/)/i.test(path) ? '' : '/' ) + path,
      headers: options.headers || {}, 
      method : method
    };
    var body = options.body;
    if(body && typeof(body) === 'object'){
      body = JSON.stringify(body);      
      if(!utils.hasHeader(ops, 'Content-Type')) ops.headers['Content-Type'] = 'application/json; charset=utf-8';
      if(!utils.hasHeader(ops, 'Content-Length')) ops.headers['Content-Length'] = Buffer.byteLength(body, 'utf8');
    }


    var req = _http.request(ops, (res)=>{
      var decoder = new StringDecoder('utf-8');
      var buffer = '';
      res.on('data', function(data) {
          buffer += decoder.write(data);
      });
      res.on('end', function() {
        buffer += decoder.end();


      if(/^(2)/i.test(res.statusCode.toString()))
          resolve({statusCode : res.statusCode , data : buffer })
        else
          reject({statusCode : res.statusCode , error : buffer })
    });
    req.on('error', (err)=>{
      reject({statusCode : 0, error : err});
    })

    req.on('timeout', (err)=>{
      reject({statusCode : 0, error : new Error('Timeout exeded.')});
    })

    if(body) req.write(Buffer.from(body).toString('utf8'));
    req.end();
  });

  return prom;
}

Upvotes: 2

Evert
Evert

Reputation: 99728

Instead of request-promise, you might also consider node-fetch as an alternative. This package implements the Fetch API which is likely to be the future of doing HTTP requests on both servers and clients, and it 100% assumes promises and it's a bit less messy than that request library.

Upvotes: 0

Related Questions