I am trying to return JSON data from an api back along my route on an express server. I am a little confused about how nodejs handles this kind of operation. I have both a function and a route in the same file, the route works because I get the view returned, and the data I want in the console. The route and method look like this:
function getData() {
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
return response.body;
};
});
};
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'About', data: getData() });
});
I want the data from getData() in my response to the route. I thought this would do it but it will only print the data to the console and I can't see the problem.
That simply isn't possible due to the asynchronous nature of http requests. You'll have to instead restructure it to have a callback.
function getData(callback) {
request(url, function (error, response, body) {
if (error) {
return callback(error);
}
if (response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
//return response.body;
callback(null, response.body);
} else {
callback(response.statusCode);
}
});
};
/* GET home page. */
router.get('/', function(req, res, next) {
getData(function (err, data) {
if (err) {
return next(err);
}
res.render('index', { title: 'About', data: data });
});
});
Related
In node.js, how to pass req, res for callBack function?
for example,
router.get("/", function (req, res) {
var content = '';
fs.readFile("./json/hello.json", function(err, file)
{
if(err)
res.render("index", {
json: content
});
else
{
content = JSON.parse(file);
res.render("index", {
json: content.name
});
}
});
});
It works well. But the code is hard to look because of lot of indentation. So I want to do like this.
router.get("/", function (req, res) {
fs.readFile("./json/hello.json", root_readFileCallBack());
});
function root_readFileCallBack(err, file) {
if (err) {
res.render("index", {
json: content
});
}
else {
content = JSON.parse(file);
res.render("index", {
json: content.name
});
}
}
Code above is better to read. But this makes error that cannot find "render" from "res" variable.
I tried to pass req, res as parameter but it doesn't work well.
How can I pass req, res to callBack fuction?
Create a closure function, the function will return a callback function for readFile function, and the function's param is res object.
router.get("/", function (req, res) {
fs.readFile("./json/hello.json", root_readFileCallBack(res));
});
function root_readFileCallBack(res) {
return function (err, file) {
if (err) {
res.render("index", {
json: content
});
}
else {
content = JSON.parse(file);
res.render("index", {
json: content.name
});
}
}
}
#hoangdv has a great answer that's commonly used in practice. Creating factory functions like that is a useful trick to learn.
Here's another way to go about achieving what you want.
router.get("/", function (req, res) {
const callback = (err, file) => root_readFileCallBack(err, file, res)
fs.readFile("./json/hello.json", callback);
});
function root_readFileCallBack(err, file, res) {
if (err) {
res.render("index", {
json: content
});
}
else {
content = JSON.parse(file);
res.render("index", {
json: content.name
});
}
}
Basically we make root_readFileCallBack() take a res parameter, then in router.get() we wrap root_readFileCallBack to modify its behavior a bit - specifically, we'll cause res to get passed in automatically whenever our new callback is called.
This is using an arrow function, but a normal function would work just fine too.
Append req/res arguments to the callback function.
router.get("/", function (req, res) {
fs.readFile("./json/hello.json", root_readFileCallBack.bind(this, req, res));
});
function root_readFileCallBack(req, res, err, file) {
if (err) {
res.render("index", {
json: content
});
}
else {
content = JSON.parse(file);
res.render("index", {
json: content.name
});
}
}
Controller code:
bina: function(req, res) {
var request = require('request');
request({
url: 'http://localhost:3000/bina/',
method: 'GET',
}, function(err, res, body) {
if (err) {
console.log(err);
} else {
var data = JSON.parse(res.body);
console.log(data);
res.render(data)
}
})
}
The data that comes with the request does not see any functions like res.view or res.render.
Error output:
res.render(data)
^
TypeError: res.render is not a function
Note:
I can see the data via console.log(data) via web service. I use Sail.js.
Here's what you are doing wrong:
You are using same variable (res) name twice. And at the res.render it taking the one which is in the nearest scope. I have renamed the res => to response and now it should work.
bina: function(req, res) {
var request = require('request');
request({
url: 'http://localhost:3000/bina/',
method: 'GET',
}, function (err, response, body) { // Changed the variable name
if (err) {
console.log(err);
}
else {
var data = JSON.parse(response.body);
console.log(data);
res.render(data)
}
})
}
Hope it solved your issue.
I'm using Node.JS with Express (and ejs for the views) and I want to call two API endpoints to render them in the same page and use the data. I've tried doing it with async but I get
ECONNRESET: Request could not be proxied!
This is my code
app.get('/profile', function(req, res) {
async.parallel([
function(next) {
var query = req.query.search;
var url = '1st url' + query;
var request = require('request');
request(url, function(error, body) {
var data = JSON.parse(body);
next(error, data);
});
},
function(next) {
request('2nd url', function(error, tlist) {
var list = JSON.parse(tlist);
next(error, list);
});
}], function(err, results) {
if (!err && results.statusCode == 200)
var data = results[0];
var list = results[1];
res.render('profile', {data: data, list: list});
});
});
Unsure about Cloud9, but if the issue is around parsing data, there's a couple of things here.
You should handle the error on each request before you attempt to parse; if parse throws an exception, your callback won't be executed:
request(url, function(error, body) {
if (error) return next(error);
var data = JSON.parse(body);
next(null, data);
});
You should probably also have a try/catch around the parse, and execute your callback with an error if there's an exception:
request(url, function(error, body) {
if (error) return next(error);
var data;
try {
data = JSON.parse(body);
} catch (e) {
return next(new Error('Unable to parse body for ' + url));
}
next(null, data);
});
Finally, your current check for results.statusCode will always return false, since it's an array, so you won't reach the end of the request. I'm guessing this is probably where the problem lies. I would also recommend passing any errors from async on to the Express error handler:
function(err, results) {
if (err) {
// pass to Express error handler...
}
var data = results[0];
var list = results[1];
res.render('profile', {data: data, list: list});
});
I am trying to do a get/query request from Angular using $resource to a specified route which will ping an API and then I should get an object with the results coming from the API.
This is a search function. See the flow of this issue:
Angular service:
angular.module('MyApp')
.factory('Search', function($resource) {
return $resource('/api/shows/:_search');
});
Ctrl:
$scope.$watch('searchStr', function (tmpStr)
{
if (!tmpStr || tmpStr.length == 0)
return 0;
// if searchStr is still the same..
// go ahead and retrieve the data
if (tmpStr === $scope.searchStr) {
Search.query({'search': $scope.searchStr})
.$promise.then(function(data) {
$scope.responseData = data;
})
}
});
View:
<input type="text" data-ng-model="searchStr">
<textarea> {{responseData}} </textarea>
Nodejs:
app.get('api/shows/:search', function(req, res, next) {
console.log(req, res);
request.get('http://thetvdb.com/api/GetSeries.php?seriesname=' + req.params.search, function (error, response, body) {
console.log(error, response, body);
});
});
there is what I need, I need to do a get request to 'api/shows/:search' and do something in order to get the results from http://thetvdb.com/api/GetSeries.php?seriesname=' + req.params.search but I am still struggling how it should be done. The search param is the string coming from Angular in order to go to the thetvdb and return what I need.
Here is an example of what it should be return in case that you are sending the string param with the word "all": http://www.thetvdb.com/api/GetSeries.php?seriesname=all&language=en
Any suggestions?
at least your nodejs route has to return the string in the response:
app.get('api/shows/:search', function(req, res, next) {
request.get('http://thetvdb.com/api/GetSeries.php?seriesname=' + req.params.search, function (error, response, body) {
console.log(error, response, body);
res.end(body);
});
});
I have the below code in my node.js app.js:
var express = require('express')
,cors = require('cors')
,app=express();
var users = require('./routes/users');
//some other codes
.....
app.use('/', routes);
app.use('/users', users );
If a request is made to /users/adduser, it will go to the users.js in the routes folder.
Now I want to add a filter which will capture all the POST requests and do some validations and only if the conditions are satisfied the POST should go to its handler.
I.e if i get a /users/adduser with a POST request, before going to the method in the users.js in the routes folder, I should be able to capture that request and stop it if the condition is not met.
UPDATE 1
Now i am having this app.use function, but in the result i am getting undefined as its not waiting till the function is returning value
app.use(function (req, res, next) {
req.db = db;
if (req.method != "POST") {
next();
}
else {
var userData = req.body;
var result = Check(userData);
if(result){
next();
}
}
});
function Check(userdata) {
var url = "someurl"+userdata.Id;
var request = require("request");
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
if (response.toJSON().body.id == userId) {
return true;
// i also tried next();
}
}
})
};
You can write a simple middleware function for that:
var validator = function(req, res, next) {
// If the request wasn't a POST request, pass along to the next handler immediately.
if (req.method !== 'POST') return next();
// Perform your validations.
Check(req.body, function(err) {
// Validation failed, or an error occurred during the external request.
if (err) return res.sendStatus(400);
// Validation passed.
return next();
});
};
function Check(userdata, callback) {
var url = "someurl"+userdata.Id;
var request = require("request");
request({ url: url, json: true }, function(error, response, body) {
if (!error && response.statusCode === 200) {
if (response.toJSON().body.id === userId) {
return callback(null, true);
}
}
return callback(new Error());
})
};
You have various points at which you can insert this middleware, which kind of depend on how exactly your app is structured.
One option:
app.use('/users', validator, users);
Or, if you have a separate router for /users (in ./routes/users.js):
router.use(validator);
Or, if you have a separate POST route for /users/adduser:
router.post('/adduser', validator, function(req, res) { ... });
In the last case you don't have to check req.method in the validator middleware because it's limited to the POST handler anyway.
You can use a route middleware assuming you use Express.
If you want to perform validations for all POST requests to /users then you can add the following middleware before the route handler:
router.use(function (req, res, next) {
if (your request is valid){
next();
} else {
//Return a response immediately
res.status(400).json({ message: "Bad request" });
}
});
Check(userdata) running asycn so you should use callback here :D
function Check(userdata,cb) {
var url = "someurl"+userdata.Id;
var request = require("request");
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
if (response.toJSON().body.id == userId) {
cb(null,true);
}
}
})
};
Then change the middleware like
var result = Check(userData,function(err,result){
if(result){
next();
}
}