I'm trying to write a couple of endpoints that will make GET and POST http requests to various backend services, the format of data is all going to be very similar so responseHandler function will be copied over and over to different route function, I wonder if there is a way to externalize responseHandler for reuse. I tried to just move it out, but then I would lose reference to res. Anyone has any tips on a more modular design?
routes['/endpoint'] = function(req, res){
console.log("Serving endpoint: /endpoint")
var params={"param": "param-value"}
var options = {
host: 'localhost',
path: '/service?param='+params.param,
method: 'GET'
};
var responseHandler = function(response) {
var data = '';
// keep track of the data you receive
response.on('data', function(chunk) {
data += chunk + "\n";
});
// finished? ok, send the data to the client in JSON format
response.on('end', function() {
res.header("Content-Type:","application/json");
res.end(data);
});
};
// make the request, and then end it, to close the connection
http.request(options, responseHandler).end();
};
generally i would think you could create a folder in your lib called responseHandlers, add a file that contains something like
var responseHandler = function(response) {
var data = '';
// keep track of the data you receive
response.on('data', function(chunk) {
data += chunk + "\n";
});
// finished? ok, send the data to the client in JSON format
response.on('end', function() {
res.header("Content-Type:","application/json");
res.end(data);
});
};
exports.Handler = responseHandler;
save that as whateverHandler.js, then create an index.js file that requires whatever.js and exports it Handler. this way if you need to add more handlers in the future you just have to add a file and update the index.js. to use, do something in the route handler like
var handler = require('./lib/responseHandlers').whateverHandler;
routes['/endpoint'] = function(req, res){
console.log("Serving endpoint: /endpoint")
var params={"param": "param-value"}
var options = {
host: 'localhost',
path: '/service?param='+params.param,
method: 'GET'
};
};
// make the request, and then end it, to close the connection
http.request(options, handler).end();
};
You could turn responseHandler into a function generator, and pass in your res object so you don't lose it:
var responseHandler = function(res) {
return function(response) {
var data = '';
// keep track of the data you receive
response.on('data', function(chunk) {
data += chunk + "\n";
});
// finished? ok, send the data to the client in JSON format
response.on('end', function() {
res.header("Content-Type:","application/json");
res.end(data);
});
};
}
And use it like this:
routes['/endpoint'] = function(req, res){
console.log("Serving endpoint: /endpoint")
var params={"param": "param-value"}
var options = {
host: 'localhost',
path: '/service?param='+params.param,
method: 'GET'
};
// make the request, and then end it, to close the connection
http.request(options, responseHandler(res)).end();
};
Related
I've got an audio file which I post to a server for translation. I've managed to create a request in postman, but I do not know how to write the file to this server. Below is the code I have got so far:
var http = require("https");
var options = {}
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
options{} is filled with method/hostname/port ofcourse. In postman I add "binary" file, but I cannot figger out how to write a file to the request in Node JS.
One solution that would easily fit into your current program without too much work is to make use of the form-data module on npm.
The form-data module makes ease of multipart requests in node. The following is a simple example of how to use.
var http = require("https");
var FormData = require('form-data');
var fs = require('fs')
var form = new FormData();
form.append('my_field', fs.createReadStream('my_audio.file'));
var options = {
host: 'your.host',
port: 443,
method: 'POST',
// IMPORTANT!
headers: form.getHeaders()
}
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
// Pipe form to request
form.pipe(req);
In a "real-world" scenario you would want to do a lot more error checking. Also, there are plenty of other http clients on npm that make this process easy as well (the request module uses form-data BTW). Check out request, and got if you are interested.
For sending a binary request the fundamentals are still the same, req is a writable stream. As such, you can pipe data into the stream, or write directly with req.write(data). Here's an example.
var http = require('https');
var fs = require('fs');
var options = {
// ...
headers: {
'Content-Type': 'application/octet-stream'
}
}
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
var audioFile = fs.createReadStream('my_audio.file', { encoding: 'binary' });
audioFile.pipe(req);
Note, that if you use the write method explicitly req.write(data) you must call req.end(). Also, the you may want to take a look at the encoding options for Node's Buffer (docs).
You can use the request package on npm.
Install the request module from npm:
npm install request --save
Then use the request module to send your request.
Have a look at https://www.npmjs.com/package/request for details on implementation.
Thanks #undefined, your answer really helped me.
I am posting my solution which worked for me to send file to another server using axios. Ignore the type specifications, I had Typescript enabled for my project.
export const fileUpload: RequestHandler = async (req: Request, res: Response, next: NextFunction) => {
const chunks: any[] = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const data = Buffer.concat(chunks);
axios.put("ANOTHER_SERVER_URL", data).then((response) => {
console.log('Success', response);
}).catch(error => {
console.log('Failure', error);
});
});
return res.status(200).json({});
};
Thanks, hope it helps!
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 using express.js and I need to make a call to HTTP GET request ,to fetch JSON data .Please suggest me some good node js/express js modules/lib to perform get/post request .
Node.js provides an extremely simple API for this functionality in the form of http.request.
var http = require('http');
//The url we want is: 'www.random.com/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
var options = {
host: 'www.random.com',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function(response) {
var str = '';
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
console.log(str);
});
}
http.request(options, callback).end();
Here I attach some more examples with POST and custom headers. If you don't need special things, I'd stick to the native code.
Besides, Request, Superagent or Requestify are pretty good libraries to use.
var express = require('express');
var app = express();
var fs = require('fs');
app.get('/', function (req, res) {
fs.readFile('./test.json', 'utf8', function (err, data) {
if (err) {
res.send({error: err});
}
res.send(data);
})
});
var server = app.listen(3001, function () {
console.log('Example app listening port 3001');
});
i'm having a problem here.
I have this piece of code:
var controller = require('./controllers/controller');
app.post('/',
controller.create,
);
And inside that i have:
exports.create = function(req, res, next) {
res.send('test');
};
Yes, i'm using a form to make a post.
The code is not working, the console will show: Can't set headers after they sent.
But if i change the code to:
var controller = require('./controllers/controller');
app.post('/', function(req, res {
res.send('test');
});
Will work!!!!! Why is that???
UPDATE:
I don't know if maybe this is causing that, but i have this following code:
var home = require('./controllers/home');
app.get('/',
home.index
);
And inside that i have this code:
exports.index = function(req, res, next) {
var url = "someurl/"
var stringify = "";
var request = https.get(url, function(response) {
response.on('data', function(json) {
stringify += json
});
response.on('end', function() {
var json = JSON.parse(stringify);
var data = json.feed.entry;
res.render('index', {
data: data
});
});
});
};
if i pass the code of the first controller to the home controller, will work.. but i need separate.
response.on('end', function() {
var json = JSON.parse(stringify);
var data = json.feed.entry;
res.render('index', {
data: data
});
});
Here you're trying to listen for when the response ends. This event only fires when the response has been sent to the client already, including the response headers. Your call to res.render fails because the response has ended. You can't then continue to send more data to the client. Anything that happens in the "end" event handler cannot write to the response stream anymore.
Im using sailsjs as a MVC for node js, i'm still learning it.
I managed to get data from my own database and use it.
But now i need/want to get data from an external rest api.
I used this in my controller:
// api/controllers/SomeController.js
test : function(res,req){
var j;
var https = require('https');
var options = {
hostname: 'testing.atlassian.net',
port: 443,
path: '/rest/api/2/search?jql=project=ABC',
method: 'GET',
headers: {'Authorization': 'Basic ' + 'SuperSecretLoginAndPassword'}
};
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function(d) {
});
});
req.end();
}
The variable d is displaying the right result.
How can i use the request results in my view?
I've searched a lot but i cant find any ways to display this in my view.
And will this be realtime updated? So if something in de rest api changes I won't have to refresh.
Sorry if this is something stupid.
Basically you'll want to wait for your request to fire its callback and then feed the fetched data into res.locals. Assuming you are fetching JSON data, you could do this:
// api/controllers/SomeController.js
test: function(req, res) {
var https = require('https');
...
https.request(options, function(response) {
var responseData = '';
response.setEncoding('utf8');
response.on('data', function(chunk){
responseData += chunk;
});
response.once('error', function(err){
// Some error handling here, e.g.:
res.serverError(err);
});
response.on('end', function(){
try {
// response available as `responseData` in `yourview`
res.locals.requestData = JSON.parse(responseData);
} catch (e) {
sails.log.warn('Could not parse response from options.hostname: ' + e);
}
res.view('yourview');
});
}).end();
}
The example code you supplied has some issues:
test: function(res,req) ... Don't mixup the controller arguments, the first is _req_uest, the second one _res_ponse.
var req = https.request ... You really do not want to override the req argument passed into your controller action. Use some other name.
https.request(options, function(res) {...} Same here. Doing this overrides res for the https.request callback scope - preventing you from using all the goodies (e.g.: res.view) supplied by the res parameter passed to your controller.
I'd guess it would make sense for you to read up on closures and callbacks:
What are Closures and Callbacks?