How to chain http.request() - node.js

I need to use require('http') (I cannot use other libraries), and I am trying to figure out how I would chain multiple http.request() together?
In my example below, I am trying to create a household with 4 people, and each person will have a pet associated to them. I have 3 routes that will createFamily, createPerson, and createPet. I also have the method createHousehold() that will take the ID's from the response of each route, and pass it down the chain (family -> person -> pet). I am not sure how I would be chaining the response of each route, and passing down the ID.
const http = require('http');
createHousehold('Smith', 4); // Creates 'Smith' family with 4 people, and each member has one pet
// Not sure how to chain requests
function createHousehold(surname, numberOfPeople) {
createFamily(surname)
.then(familyId => {
for (let i = 0; i < numberOfPeople; i++) {
createPerson(familyId)
.then(personId => createPet(personId));
}
});
}
function createFamily(surName) {
const data = JSON.stringify({
config: { surName }
});
const options = {
host: 'myProxyHost.com',
port: '8080',
path: '/v1/family',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const request = http.request(options, response => {
let data = '';
response.on('data', chunk => data += chunk);
return (response.on('end', () => JSON.parse(data).id));
});
request.on('error', error => console.log('ERROR - createFamily(): ', error.message));
request.write(data);
request.end();
return request;
}
function createPerson(familyId) {
const data = JSON.stringify({
config: { familyId }
});
const options = {
host: 'myProxyHost.com',
port: '8080',
path: '/v1/person',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const request = http.request(options, response => {
let data = '';
response.on('data', chunk => data += chunk);
return (response.on('end', () => JSON.parse(data).id));
});
request.on('error', error => console.log('ERROR - createPerson(): ', error.message));
request.write(data);
request.end();
return request;
}
function createPet(personId) {
const data = JSON.stringify({
config: { personId }
});
const options = {
host: 'myProxyHost.com',
port: '8080',
path: '/v1/pet',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const request = http.request(options, response => {
let data = '';
response.on('data', chunk => data += chunk);
return (response.on('end', () => JSON.parse(data).id));
});
request.on('error', error => console.log('ERROR - createPet(): ', error.message));
request.write(data);
request.end();
return request;
}

For example with a proxy server, you would pipe one request (readable) into another request (writable).
If you are just doing serial requests, I would just wrap them in a promise, or use the async library.
function createPet(personId) {
return new Promise((resolve,reject) => {
const data = JSON.stringify({
config: { personId }
});
const options = {
host: 'myHost.com',
port: '8080',
path: '/v1/pet',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const request = http.request(options, response => {
let data = '';
response.on('data', chunk => data += chunk);
response.once('end', () => resolve(data)); // ! resolve promise here
});
request.once('error', err => {
console.log('ERROR - createPet(): ', err.message || err);
reject(err); // ! if promise is not already resolved, then we can reject it here
});
request.write(data);
request.end();
});
}
and use it like so:
createHousehold(id)
.then(createFamily)
.then(createPerson)
.then(createPet);
if you want to do things in parallel, then use Promise.all()..or use the async library.
For seeding a database, I highly recommend async.autoInject, you will quickly see why:
https://caolan.github.io/async/v2/docs.html#autoInject
you can use it like so:
const seedDatabase = () => {
return async.autoInject({ // returns a promise
async createHouseHold(){
return somePromise();
},
async createFamily(createHouseHold){
return somePromise();
},
async createPerson(createFamily){
return somePromise();
},
async createPet(createPerson){
return somePromise();
}
});
}

Related

Wait for http post to end using promise

I am learning to use http post and trying to wait for it to end using promise. But I can't get it to work, please help:
var http = require('http');
const auth = () => {
var post_data = JSON.stringify({
"username": "aaa",
"password": "bbb"
});
const options = {
host: 'http://1.1.1.1',
path: '/v1/authentication',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': post_data.length
}
};
const req = http.request(options, (res) => {
res.setEncoding('utf8');
var body = '';
res.on('data', d => {
body += chunk;
});
res.on('end', function() {
console.log("Response body", body);
});
});
req.on('error', error => {
console.error("error", error);
});
req.write(post_data)
req.end();
return Promise.resolve("!!");
};
exports.helloWorld = (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
return auth().then((res)=>{
res.status(200).send(res);
});
};
Entry point is the hellWorld function. What should I do to wait for the http post to finish and get the response result using promise?
here i did some for get api call.
try {
const auth = () => {
return new Promise((resolve, reject) => {
const options = {
host: 'api.github.com',
path: '/orgs/nodejs',
port: 443,
method: 'GET',
headers: {
'User-Agent': 'request'
}
};
const req = https.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', d => {
resolve(d)
});
});
req.on('error', error => {
console.error("error", error);
});
req.end();
})
};
const data = await auth()
console.log('should execute first', data)
console.log('should executed after http call')
} catch (e) {
console.log(e)
}
you can modify above code with your, just you have to wrap your http call inside Promises.
comment down if any its a solution, and mark as a solution

how to use async await with https post request

I am finding way out to use async / await with https post. Please help me out. I have posted my https post code snippet below.how do I use async await with this.
const https = require('https')
const data = JSON.stringify({
todo: 'Buy the milk'
})
const options = {
hostname: 'flaviocopes.com',
port: 443,
path: '/todos',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
const req = https.request(options, (res) => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {
process.stdout.write(d)
})
})
req.on('error', (error) => {
console.error(error)
})
req.write(data)
req.end()
Basically, you can write a function which will return a Promise and then you can use async/await with that function. Please see below:
const https = require('https')
const data = JSON.stringify({
todo: 'Buy the milk'
});
const options = {
hostname: 'flaviocopes.com',
port: 443,
path: '/todos',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
},
};
async function doSomethingUseful() {
// return the response
return await doRequest(options, data);
}
/**
* Do a request with options provided.
*
* #param {Object} options
* #param {Object} data
* #return {Promise} a promise of request
*/
function doRequest(options, data) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding('utf8');
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
resolve(JSON.parse(responseBody));
});
});
req.on('error', (err) => {
reject(err);
});
req.write(data)
req.end();
});
}
I had this problem also, found this post, and used the solution from Rishikesh Darandale (here).
The await documentation states The await operator is used to wait for a Promise. Returning the promise from a function is not required. You can just create a promise and call await on it.
async function doPostToDoItem(myItem) {
const https = require('https')
const data = JSON.stringify({
todo: myItem
});
const options = {
hostname: 'flaviocopes.com',
port: 443,
path: '/todos',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
},
};
let p = new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding('utf8');
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
resolve(JSON.parse(responseBody));
});
});
req.on('error', (err) => {
reject(err);
});
req.write(data)
req.end();
});
return await p;
}
You can use async-await with Promises only and Node's core https module does not have build in promise support. So you first have to convert it into the promise format and then you can use async-await with it.
https://www.npmjs.com/package/request-promise
This module has converted the core http module into promisified version. You can use this.

NodeJS: callback not a function

I've checked the other questions but none form an answer to mine. In other words: no answer works.
I've got two modules: app and logic. App passes a callback to logic, which logic then calls. However I get the error "cb is not a function". That's it, no more information.
This is the function in app.js that poses the callback:
app.get('/assignments/open/:student', function (request, response) {
var callback = function(mail, jobtitle) {
response.status(200).json(logic.getOpenAssignments(request.params.student));
}
logic.examine(request, callback);
});
This is the function in logic.js that calls the callback:
examine: function (request, cb) {
var options = {
host: 'graph.microsoft.com',
path: '/v1.0/me',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': request.get('Authorization')
}
};
var req = http.get(options, function(res) {
res.on('data', function(chunk) {
chunk = JSON.parse(chunk);
mail = chunk['mail'];
jobtitle = chunk["jobTitle"];
cb(mail, jobtitle);
})
});
},
Any solutions? Thanks in advance!
Edit:
Following code doesn't work:
examine: function (request, cb) {
var options = {
host: 'graph.microsoft.com',
path: '/v1.0/me',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': request.get('Authorization')
}
};
var req = http.get(options, res => {
res.on('data', chunk => {
chunk = JSON.parse(chunk);
mail = chunk['mail'];
jobtitle = chunk["jobTitle"];
cb(mail, jobtitle);
})
});
},
Following code also doesn't work, does this mean the scope is not the problem?
examine: function (request, cb) {
var options = {
host: 'graph.microsoft.com',
path: '/v1.0/me',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': request.get('Authorization')
}
};
cb('hehe', 'hoho');
},
If you're using ES6, try to use arrow function to pass the scope to http.get's callback function:
var req = http.get(options, res => {
res.on('data', chunk => {
chunk = JSON.parse(chunk);
mail = chunk['mail'];
jobtitle = chunk["jobTitle"];
cb(mail, jobtitle);
})
});
Or you could also pass cb to your callback function :
var req = http.get(options, function(res, cb) {
res.on('data', function(chunk, cb) {
chunk = JSON.parse(chunk);
mail = chunk['mail'];
jobtitle = chunk["jobTitle"];
cb(mail, jobtitle);
})
});
Maybe the issue is, you are not waiting for data chunk to process. Try the following snippet. Added a way to convert http.get to promises(very common) and this avoids passing all the messy callbacks.
//put this somewhere inside where examine is located.
logic.httpGet = options => {
return new Promise((resolve, reject) => {
http.get(options, res => {
res.setEncoding('utf8');
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => resolve(body));
}).on('error', reject);
});
};
//convert your route like this
app.get('/assignments/open/:student', function (request, response) {
cont options = {
host: 'graph.microsoft.com',
path: '/v1.0/me',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': request.get('Authorization')
}
};
return logic
.httpGet(options)
.then((data)=>{
const data =JSON.parse(data);
//i don't see where you use the processed data but do whatever you want below here
repsonse.status(200).json(logic.getOpenAssignments(request.params.student))
})
})

How do I wait for a json to load before using it on node.js? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
[EDIT]
I figured it out. The code ends up like this:
//getTrelloJSON.js
var request = require('request');
'use strict';
function getProjJSON(requestURL, callback){
request.get({
url: requestURL,
json: true,
headers: {'User-Agent': 'request'}
}, (err, res, data) => {
if (err) {
console.log('Error:', err);
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode);
} else {
callback(data);
}
});
}
module.exports.getProjJSON = getProjJSON;
And
//showData.js
var getJSON = require('./getTrelloJSON');
getJSON.getProjJSON('https://trello.com/b/saDpzgbw/ld40-gem-sorceress.json', (result) => {
var lists = result.lists;
console.log(lists);
});
I run node showData.js and it gets the json and then I can manipulate it as needed. I printed just to show it works.
[EDIT END]
I'm new to node.js and I am facing a noob problem.
This code is supposed to request a JSON from a public trello board and return an object with a section of trello's json (lists section).
The first console.log() does not work but the second does.
How do I make it wait for the completion of getProjJSON() before printing it?
var request = require('request');
'use strict';
//it fails
console.log(getProjJSON('https://trello.com/b/saDpzgbw/ld40-gem-sorceress.json'));
function getProjJSON(requestURL){
request.get({
url: requestURL,
json: true,
headers: {'User-Agent': 'request'}
}, (err, res, data) => {
if (err) {
console.log('Error:', err);
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode);
} else {
//it works
console.log(data.lists);
return data.lists;
}
});
}
Node.js is all about callbacks.
And here you just not register the callbacks for data.
var client = require('http');
var options = {
hostname: 'host.tld',
path: '/{uri}',
method: 'GET', //POST,PUT,DELETE etc
port: 80,
headers: {} //
};
//handle request;
pRequest = client.request(options, function(response){
console.log("Code: "+response.statusCode+ "\n Headers: "+response.headers);
response.on('data', function (chunk) {
console.log(chunk);
});
response.on('end',function(){
console.log("\nResponse ended\n");
});
response.on('error', function(err){
console.log("Error Occurred: "+err.message);
});
});
or here is a full example, hope this solve your problem
const postData = querystring.stringify({
'msg' : 'Hello World!'
});
const options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
console.log(`res_code: ${res.statusCode}`);
console.log(`res_header: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`res_data: ${chunk}`);
});
res.on('end', () => {
console.log('end of response');
});
});
req.on('error', (e) => {
console.error(`response error ${e.message}`);
});
//write back
req.write(postData);
req.end();

Stream application/json POST request

I have working code to buffer a .json file and then POST that data to a server.
fs.readFile(filePath, 'utf-8', function (err, buf) {
if(err){
reject(err);
}
else{
const req = http.request({
method: 'POST',
host: 'localhost',
path: '/event',
port: '4031',
headers: {
'Content-Type': 'application/json',
'Content-Length': buf.length
}
});
req.on('error', reject);
var data = '';
req.on('response', function (res) {
res.setEncoding('utf8');
res.on('data', function ($data) {
data += $data
});
res.on('end', function () {
data = JSON.parse(data);
console.log('data from SC:', data);
//call fn on data and if it passes we are good
resolve();
});
});
// write data to request body
req.write(buf);
req.end();
}
});
what I would like to do instead is to avoid buffering it, and just use fs.createReadStream, something like so:
fs.createReadStream(filePath, 'utf-8', function (err, strm) {
if(err){
reject(err);
}
else{
const req = http.request({
method: 'POST',
host: 'localhost',
path: '/event',
port: '4031',
headers: {
'Content-Type': 'application/json',
// 'Content-Length': buf.length
}
});
req.on('error', reject);
var data = '';
req.on('response', function (res) {
res.setEncoding('utf8');
res.on('data', function ($data) {
data += $data
});
res.on('end', function () {
data = JSON.parse(data);
console.log('data from SC:', data);
//call fn on data and if it passes we are good
resolve();
});
});
// write data to request body
req.write(strm);
req.end();
}
});
but that doesn't quite work? Is it possible to do this?
This seems to work, but not sure if it's 100% correct
const strm = fs.createReadStream(filePath);
const req = http.request({
method: 'POST',
host: 'localhost',
path: '/event',
port: '4031',
headers: {
'Content-Type': 'application/json',
}
});
req.on('error', reject);
var data = '';
req.on('response', function (res) {
res.setEncoding('utf8');
res.on('data', function ($data) {
data += $data
});
res.on('end', function () {
data = JSON.parse(data);
resolve();
});
});
// write data to request body
strm.pipe(req);

Resources