How can I make an HTTP request from within Node.js or Express.js? I need to connect to another service. I am hoping the call is asynchronous and that the callback contains the remote server's response.
Here is a snippet of some code from a sample of mine. It's asynchronous and returns a JSON object. It can do any form of GET request.
Note that there are more optimal ways (just a sample) - for example, instead of concatenating the chunks you put into an array and join it etc... Hopefully, it gets you started in the right direction:
const http = require('http');
const https = require('https');
/**
* getJSON: RESTful GET request returning JSON object(s)
* #param options: http options object
* #param callback: callback to pass the results JSON object(s) back
*/
module.exports.getJSON = (options, onResult) => {
console.log('rest::getJSON');
const port = options.port == 443 ? https : http;
let output = '';
const req = port.request(options, (res) => {
console.log(`${options.host} : ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
output += chunk;
});
res.on('end', () => {
let obj = JSON.parse(output);
onResult(res.statusCode, obj);
});
});
req.on('error', (err) => {
// res.send('error: ' + err.message);
});
req.end();
};
It's called by creating an options object like:
const options = {
host: 'somesite.com',
port: 443,
path: '/some/path',
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
And providing a callback function.
For example, in a service, I require the REST module above and then do this:
rest.getJSON(options, (statusCode, result) => {
// I could work with the resulting HTML/JSON here. I could also just return it
console.log(`onResult: (${statusCode})\n\n${JSON.stringify(result)}`);
res.statusCode = statusCode;
res.send(result);
});
UPDATE
If you're looking for async/await (linear, no callback), promises, compile time support and intellisense, we created a lightweight HTTP and REST client that fits that bill:
Microsoft typed-rest-client
Try using the simple http.get(options, callback) function in node.js:
var http = require('http');
var options = {
host: 'www.google.com',
path: '/index.html'
};
var req = http.get(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
// Buffer the body entirely for processing as a whole.
var bodyChunks = [];
res.on('data', function(chunk) {
// You can process streamed parts here...
bodyChunks.push(chunk);
}).on('end', function() {
var body = Buffer.concat(bodyChunks);
console.log('BODY: ' + body);
// ...and/or process the entire body here.
})
});
req.on('error', function(e) {
console.log('ERROR: ' + e.message);
});
There is also a general http.request(options, callback) function which allows you to specify the request method and other request details.
Request and Superagent are pretty good libraries to use.
note: request is deprecated, use at your risk!
Using request:
var request=require('request');
request.get('https://someplace',options,function(err,res,body){
if(err) //TODO: handle err
if(res.statusCode === 200 ) //etc
//TODO Do something with response
});
You can also use Requestify, a really cool and very simple HTTP client I wrote for nodeJS + it supports caching.
Just do the following for GET method request:
var requestify = require('requestify');
requestify.get('http://example.com/api/resource')
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
response.getBody();
}
);
This version is based on the initially proposed by bryanmac function which uses promises, better error handling, and is rewritten in ES6.
let http = require("http"),
https = require("https");
/**
* getJSON: REST get request returning JSON object(s)
* #param options: http options object
*/
exports.getJSON = function (options) {
console.log('rest::getJSON');
let reqHandler = +options.port === 443 ? https : http;
return new Promise((resolve, reject) => {
let req = reqHandler.request(options, (res) => {
let output = '';
console.log('rest::', options.host + ':' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function (chunk) {
output += chunk;
});
res.on('end', () => {
try {
let obj = JSON.parse(output);
// console.log('rest::', obj);
resolve({
statusCode: res.statusCode,
data: obj
});
}
catch (err) {
console.error('rest::end', err);
reject(err);
}
});
});
req.on('error', (err) => {
console.error('rest::request', err);
reject(err);
});
req.end();
});
};
As a result you don't have to pass in a callback function, instead getJSON() returns a promise. In the following example the function is used inside of an ExpressJS route handler
router.get('/:id', (req, res, next) => {
rest.getJSON({
host: host,
path: `/posts/${req.params.id}`,
method: 'GET'
}).then(({ statusCode, data }) => {
res.json(data);
}, (error) => {
next(error);
});
});
On error it delegates the error to the server error handling middleware.
Unirest is the best library I've come across for making HTTP requests from Node. It's aiming at being a multiplatform framework, so learning how it works on Node will serve you well if you need to use an HTTP client on Ruby, PHP, Java, Python, Objective C, .Net or Windows 8 as well. As far as I can tell the unirest libraries are mostly backed by existing HTTP clients (e.g. on Java, the Apache HTTP client, on Node, Mikeal's Request libary) - Unirest just puts a nicer API on top.
Here are a couple of code examples for Node.js:
var unirest = require('unirest')
// GET a resource
unirest.get('http://httpbin.org/get')
.query({'foo': 'bar'})
.query({'stack': 'overflow'})
.end(function(res) {
if (res.error) {
console.log('GET error', res.error)
} else {
console.log('GET response', res.body)
}
})
// POST a form with an attached file
unirest.post('http://httpbin.org/post')
.field('foo', 'bar')
.field('stack', 'overflow')
.attach('myfile', 'examples.js')
.end(function(res) {
if (res.error) {
console.log('POST error', res.error)
} else {
console.log('POST response', res.body)
}
})
You can jump straight to the Node docs here
Check out shred. It's a node HTTP client created and maintained by spire.io that handles redirects, sessions, and JSON responses. It's great for interacting with rest APIs. See this blog post for more details.
Check out httpreq: it's a node library I created because I was frustrated there was no simple http GET or POST module out there ;-)
For anyone who looking for a library to send HTTP requests in NodeJS, axios is also a good choice. It supports Promises :)
Install (npm): npm install axios
Example GET request:
const axios = require('axios');
axios.get('https://google.com')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
Github page
Update 10/02/2022
Node.js integrates fetch in v17.5.0 in experimental mode. Now, you can use fetch to send requests just like you do on the client-side. For now, it is an experimental feature so be careful.
If you just need to make simple get requests and don't need support for any other HTTP methods take a look at: simple-get:
var get = require('simple-get');
get('http://example.com', function (err, res) {
if (err) throw err;
console.log(res.statusCode); // 200
res.pipe(process.stdout); // `res` is a stream
});
Use reqclient: not designed for scripting purpose
like request or many other libraries. Reqclient allows in the constructor
specify many configurations useful when you need to reuse the same
configuration again and again: base URL, headers, auth options,
logging options, caching, etc. Also has useful features like
query and URL parsing, automatic query encoding and JSON parsing, etc.
The best way to use the library is create a module to export the object
pointing to the API and the necessary configurations to connect with:
Module client.js:
let RequestClient = require("reqclient").RequestClient
let client = new RequestClient({
baseUrl: "https://myapp.com/api/v1",
cache: true,
auth: {user: "admin", pass: "secret"}
})
module.exports = client
And in the controllers where you need to consume the API use like this:
let client = require('client')
//let router = ...
router.get('/dashboard', (req, res) => {
// Simple GET with Promise handling to https://myapp.com/api/v1/reports/clients
client.get("reports/clients")
.then(response => {
console.log("Report for client", response.userId) // REST responses are parsed as JSON objects
res.render('clients/dashboard', {title: 'Customer Report', report: response})
})
.catch(err => {
console.error("Ups!", err)
res.status(400).render('error', {error: err})
})
})
router.get('/orders', (req, res, next) => {
// GET with query (https://myapp.com/api/v1/orders?state=open&limit=10)
client.get({"uri": "orders", "query": {"state": "open", "limit": 10}})
.then(orders => {
res.render('clients/orders', {title: 'Customer Orders', orders: orders})
})
.catch(err => someErrorHandler(req, res, next))
})
router.delete('/orders', (req, res, next) => {
// DELETE with params (https://myapp.com/api/v1/orders/1234/A987)
client.delete({
"uri": "orders/{client}/{id}",
"params": {"client": "A987", "id": 1234}
})
.then(resp => res.status(204))
.catch(err => someErrorHandler(req, res, next))
})
reqclient supports many features, but it has some that are not supported by other
libraries: OAuth2 integration and logger integration
with cURL syntax, and always returns native Promise objects.
If you ever need to send GET request to an IP as well as a Domain (Other answers did not mention you can specify a port variable), you can make use of this function:
function getCode(host, port, path, queryString) {
console.log("(" + host + ":" + port + path + ")" + "Running httpHelper.getCode()")
// Construct url and query string
const requestUrl = url.parse(url.format({
protocol: 'http',
hostname: host,
pathname: path,
port: port,
query: queryString
}));
console.log("(" + host + path + ")" + "Sending GET request")
// Send request
console.log(url.format(requestUrl))
http.get(url.format(requestUrl), (resp) => {
let data = '';
// A chunk of data has been received.
resp.on('data', (chunk) => {
console.log("GET chunk: " + chunk);
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log("GET end of response: " + data);
});
}).on("error", (err) => {
console.log("GET Error: " + err);
});
}
Don't miss requiring modules at the top of your file:
http = require("http");
url = require('url')
Also bare in mind that you may use https module for communicating over secured network. so these two lines would change:
https = require("https");
...
https.get(url.format(requestUrl), (resp) => { ......
## you can use request module and promise in express to make any request ##
const promise = require('promise');
const requestModule = require('request');
const curlRequest =(requestOption) =>{
return new Promise((resolve, reject)=> {
requestModule(requestOption, (error, response, body) => {
try {
if (error) {
throw error;
}
if (body) {
try {
body = (body) ? JSON.parse(body) : body;
resolve(body);
}catch(error){
resolve(body);
}
} else {
throw new Error('something wrong');
}
} catch (error) {
reject(error);
}
})
})
};
const option = {
url : uri,
method : "GET",
headers : {
}
};
curlRequest(option).then((data)=>{
}).catch((err)=>{
})
Related
I have been working on setting up a cache server for my nodejs application.
Below is my project I am working on. Simple one.
I have an external API that gives me a JSON response for a GET request.
I want to store those key and JSON (value) in a redis cache server. And I am able to do that.
Now when the GET request is called it comes to my node URL --> External API <-- JSON as response (which is stored in the cache).
For next request if the same GET comes, it goes to cache to fetch the key/value pair.
But here, if my cache server is not reachable or available, I have to go to API again to fetch the value.
I tried the code like below and it is failing when the Cache server is shutdown. How do I do a if-loop for getting the connection status of the redis cache server?
Below is my controller.js
function getRedisCacheConnection(){
const REDIS_PORT = process.env.PORT || 6379;
const client = redis.createClient(REDIS_PORT);
client.on('connect', function() {
console.log('Redis client connected');
console.log(`${client.connected}`);
});
client.on('error', function (err) {
console.log('Something went wrong ' + err);
})
return client;
}
exports.getCityCode = (req,res)=>{
var originCity = req.query.origincity;
var originState = req.query.originstate;
function setReponse(originCity, response){
return response;
}
const options = {
method: 'GET',
uri: `http://webapi.external.com/api/v3/locations?name=${originCity}`,
headers: {
'Content-Type': 'application/json'
}
}
request(options).then(function (response){
res.status(200).send(setReponse(originCity, response));
const client1 = getRedisCacheConnection();
console.dir("setting key to Cache " + originCity);
client1.setex(originCity, 3600, response);
});
}
exports.Cache = (req,res,next) => {
const originCity = req.query.origincity;
function setReponse(originCity, response){
return response;
}
const client1 = getRedisCacheConnection();
client1.get(originCity, (err,data) =>{
if(err) throw err;
if(data !== null){
console.dir(originCity + " Getting Key from Cache");
res.status(200).send(setReponse(originCity,data));
}
else{
next();
}
});
}
Here is my router.js
app.get('/citycode/', city.Cache, city.getCityCode);
you are throwing an error:
if(err) throw err;
Log the error and don't create an exception.
I'm trying to upload images from a url to the server safely.
var http = require('http');
var fs = require('fs');
var path = require('path')
var download = function(url, dest, filename, cb) {
var file = fs.createWriteStream(dest + "/" + filename + path.extname(url));
var request = http.get(url, function(response) {
response.pipe(file);
file.on('error', function(err) {
console.log(err.message);
file.end();
});
file.on('finish', function() {
file.close(cb);
});
}).on('error', function(err) { // Handle errors
fs.unlink(dest);
if (cb) cb(err.message);
});
}
var url = "https://vignette.wikia.nocookie.net/spongebob/images/d/d7/SpongeBob_stock_art.png";
download(url, 'downloadedAssets/imgs', 'spongebob', function onComplete(err) {
if (err) {
console.log(err.message);
} else {
console.log('image uploaded to server');
}
});
Which crashes the server
TypeError [ERR_INVALID_PROTOCOL]: Protocol "https:" not supported.
Expected "http:"
I understand I can't upload https files, but why does it crash the server instead of executing file.on('error') then ending the file.
I also tried try/catch and same thing
It crashes at http.get(url,function(){..}) because you are using http module. You need to use https module to do the get request to a https url.
const https = require('https');
https.get('https://encrypted.google.com/', (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
}).on('error', (e) => {
console.error(e);
});
You can use request module which support both http and https. From the doc:
Request is designed to be the simplest way possible to make http
calls. It supports HTTPS and follows redirects by default.const request = require('request');
const options = {
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
const info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
}
request(options, callback);
Or to use Promises out of the box, use axios.
fs.createWriteStream is working absolutely fine so it is not throwing exceptions or errors.
The error is coming from http.get() method because you are trying to access 'https' using http module. Put debugger in http.get() module, you will definitely get error thrown.
I need to fetch the data from two different API endpoints, and after both data is fetched I should do something with those data (ie. compare the data from two sources).
I know how to fetch the data from one API, and then call the callback function to do something with the data. I am doing this as follows.
function getJSON(options, cb) {
http.request(options, function(res){
var body = "";
res.on("data", function(chunk){
body += chunk;
});
res.on("end", function(){
var result = JSON.parse(body);
cb(null, result);
});
res.on("error", cb);
})
.on("error", cb)
.end();
}
var options = {
host: "api.mydata1.org",
port: 8080,
path: "/data/users/3",
method: "GET"
}
getJSON(options, function(err, result) {
if (err) {
return console.log("Error getting response: ", err);
}
// do something with the data
});
Now, what I would want to have something like:
var options2 = {
host: "api.mydata2.org",
port: 8080,
path: "/otherdata/users/3",
method: "GET"
}
Those would be the options to connect to the other API, and have a single callback function that would get called whenever the data from both APIs is loaded. How can I do that?
I'd suggest using the request-promise module which returns promises that represent the async operations and you can then use Promise.all() to track when both of them are done and then access their results:
const rp = require('request-promise');
function getJSON(options) {
return rp(options).then(body => {
return JSON.parse(body);
});
}
let options1 = {
host: "api.mydata1.org",
port: 8080,
path: "/data/users/3",
method: "GET"
};
let options2 = {
host: "api.mydata2.org",
port: 8080,
path: "/otherdata/users/3",
method: "GET"
};
Promise.all([getJSON(options1), getJSON(options2)]).then(results => {
// process results here
console.log(results[0]); // from options1 request
console.log(results[1]); // from options2 request
}).catch(err => {
// process error here
console.log(err);
});
You can read about Promise.all() on MDN. There are lots and lots of articles and tutorials about promises in general on the web. Here's one. They are standard in ES6 Javascript and have been available for years in ES5 through libraries. They are the chosen scheme in the Javascript language for managing or coordinating asynchronous operations. If you're doing any async programming in Javascript (which pretty much all node.js programming does), then you should learn promises.
I am trying to retrieve data from a REST API in the server side (.js) and display it in my view (.jade)
I was able to get the data but was not able to send it to the view .
This is how my code looks like :
var BugData ='initial data' ;
var https = require('https');
var optionsget = {
rejectUnauthorized: false,
host : 'My host', // here only the domain name
// (no http/https !)
port : 443,
path : 'Mypath', // the rest of the url with parameters if needed
method : 'GET' // do GET
};
console.info('Options prepared:');
console.info(optionsget);
console.info('Do the GET call');
// do the GET request
var reqGet = https.request(optionsget, function(res) {
console.log("statusCode: ", res.statusCode);
res.on('data', function(d) {
console.info('GET result:\n');
BugData =d;
console.log('Show Data : ***** \n' +d);
});
});
reqGet.end();
reqGet.on('error', function(e) {
console.error(e);
});
res.render('index', { ab:BugData});
BugData (was defined before )is the variable i am trying to send to the view but for some reasons it is empty and does not contain the variable 'd'
Does anyone know why or how can i solve this ?
Thanks
There is no need to write that long code.
Be simple, follow these steps:
1) install request package:
npm install --save request
2) outside of router add:
var request = require('request');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
3) use this code inside router:
request.get({url: 'https://my-host/Mypath'}, function(err, response, body) {
var data = {};
if (err) {
console.error(err);
data.err = err;
}
data.ab = body;
console.log('Data: ', data);
res.render('index', data);
});
I am really getting crazy looking for this over the web and stackoverflow.
Other posts about this topic talk of http request, not httpS.
I'm coding server side with node.js and I need to make an https request to another website to login
If I use postman tool in chrome trying with https://user:pass#webstudenti.unica.it/esse3/auth/Logon.do everything works fine and I log in.
If I use request library in node I can't login and I get a page with a custom error message about an error in my getting/sending data.
Maybe I am wrong setting the options to pass to request.
var request = require('request');
var cheerio = require('cheerio');
var user = 'xxx';
var pass = 'yyy';
var options = {
url : 'https://webstudenti.unica.it',
path : '/esse3/auth/Logon.do',
method : 'GET',
port: 443,
authorization : {
username: user,
password: pass
}
}
request( options, function(err, res, html){
if(err){
console.log(err)
return
}
console.log(html)
var $ = cheerio.load(html)
var c = $('head title').text();
console.log(c);
})
http://jsfiddle.net/985bs0sc/1/
You're not setting your http auth options correctly (namely authorization should instead be auth). It should look like:
var options = {
url: 'https://webstudenti.unica.it',
path: '/esse3/auth/Logon.do',
method: 'GET',
port: 443,
auth: {
user: user,
pass: pass
}
}
http/https should make no difference in the authentication. Most likely your user/pass needs to be base64 encoded. Try
var user = new Buffer('xxx').toString('base64');
var pass = new Buffer('yyy').toString('base64');
See: https://security.stackexchange.com/questions/29916/why-does-http-basic-authentication-encode-the-username-and-password-with-base64
Don't use npm package request because it is deprecated, use Node native https instead
const https = require('https')
var options = {
host: 'test.example.com',
port: 443,
path: '/api/service/'+servicename,
// authentication headers
headers: {
'Authorization': 'Basic ' + new Buffer(username + ':' + passw).toString('base64')
}
};
//this is the call
request = https.get(options, function(res){
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
req.on('error', error => {
console.error(error)
})
req.end()
With the updated version, I am able to make https call with basic auth.
var request = require('request');
request.get('https://localhost:15672/api/vhosts', {
'auth': {
'user': 'guest',
'pass': 'guest',
'sendImmediately': false
}
},function(error, response, body){
if(error){
console.log(error)
console.log("failed to get vhosts");
res.status(500).send('health check failed');
}
else{
res.status(200).send('rabbit mq is running');
}
})
Use Node.js' URL module to build your URL object. The httpsAgent module is required if you are calling servers w self-signed certificates.
const https = require('https');
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
// Allow SELF_SIGNED_CERT, aka set rejectUnauthorized: false
let options = {
agent: httpsAgent
}
let address = "10.10.10.1";
let path = "/api/v1/foo";
let url = new URL(`https://${address}${path}`);
url.username = "joe";
url.password = "password123";
url.agent = httpsAgent
let apiCall = new Promise(function (resolve, reject) {
var data = '';
https.get(url, options, res => {
res.on('data', function (chunk){ data += chunk })
res.on('end', function () {
resolve(data);
})
}).on('error', function (e) {
reject(e);
});
});
try {
let result = await apiCall;
} catch (e) {
console.error(e);
} finally {
console.log('We do cleanup here');
}