How can I pass an array to the server when using $http? - node.js

I have an array of ids that I want to pass to the server. The other answers I have seen online describe how to pass these ids as query parameters in the url. I prefer not to use this method because there may be lots of ids. Here is what I have tried:
AngularJS:
console.log('my ids = ' + JSON.stringify(ids)); // ["482944","335392","482593",...]
var data = $.param({
ids: ids
});
return $http({
url: 'controller/method',
method: "GET",
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.success(function (result, status, headers, config) {
return result;
})
Node.js:
app.get('/controller/method', function(req, res) {
console.log('my ids = ' + JSON.stringify(req.body.ids)); // undefined
model.find({
'id_field': { $in: req.body.ids }
}, function(err, data){
console.log('ids from query = ' + JSON.stringify(data)); // undefined
return res.json(data);
});
});
Why am I getting undefined on the server side? I suspect it's because of my use of $.params, but I'm not sure.

In Rest GET methods uses the URL as the method to transfer information if you want to use the property data in your AJAX call to send more information you need to change the method to a POST method.
So in the server you change your declaration to:
app.post( instead of app.get(

If you're using ExpressJS server-side, req.body only contains data parsed from the request's body.
With GET requests, data is instead sent in the query-string, since they aren't expected to have bodies.
GET /controller/method?ids[]=482944&ids[]=...
And, the query-string is parsed and assigned to req.query instead.
console.log('my ids = ' + JSON.stringify(req.query.ids));
// ["482944","335392","482593",...]

Related

Node - Making a call to an ExpressJS route, and need to do a GET request to an API in it

I'm making a cryptocurrency dashboard for a project, and I'm completely new to Node and Express. This is what I have currently
app.get('/search', function(req,res){
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(req.url);
data = []
var options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (data, response) => {
response.on('data', d => {
data.push(d);
})
});
console.log(data);
request.end();
res.end();
})
The idea is on my front end, I have a button that when clicked, will make a request to the CoinAPI api, getting all reported assets and current values. I'm not quite sure how I'm supposed to send that response data back to my frontend as a response. So, I tried to pull the response data out of the JSON that gets returned by the https.request line. I have a data array data = [] as you can see at the top of my code.
I originally had my request set up like:
const request = https.request(options, response => {
but when I would try to push d onto data, I console logged and the data array was empty. This makes sense, the data array is out of scope of the request function, so data doesn't get updated. But when I tried to pass data into the function, I errored out.
Basically I want to be able to send the JSON data back to my front end after making the request to CoinAPI. If I do process.stdout.write(d) in my https.request callback, I do actually see the coinapi data coming back. I just don't know how to send it to the front end as part of my response.
Issues:
The use of (data, response) is incorrect. The first and only argument is response so it should be (response).
The on('data') event receives buffered data. Concatenating it to a final string is the standard usage, not appending an array.
You're missing an on('end') which you should use to output the final data or do any processing.
Using res.write you're sending a text/html content type and some content which you don't want if the goal is to output JSON which the frontend can parse and use.
Missing an error handler for the API call.
Complete updated code:
app.get('/search', function(req,res){
let str = '';
const options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (response) => {
response.on('data', d => {
str += d;
});
response.on('end', () => {
try {
let obj = JSON.parse(str);
// do any manipulation here
res.json(obj);
} catch(e){
console.log(e);
res.status(500).json({ message: 'Something went wrong - parse error' });
}
});
});
request.end();
request.on('error', (e) => {
console.log(e);
res.status(500).json({ message: 'Something went wrong - req error' });
});
});
I added a JSON.parse() to show how you'd handle that if you wanted to do some manipulation of the data before sending it to the frontend. If you simply want to return the exact response of the coin API then use an end event like:
response.on('end', () => {
res.json(str);
});
To send JSON data back to the client as response all you need to do is :
return res.json(data);
Its as simple as this. :)

ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client at ServerResponse

I am trying to create a simple REST API with NodeJS and Express without any database. I have stored all of my data in JSON files.
The data is in the form of an array of objects.
I have paths like fund-name/:portId
so I am doing this:
const fundName = require('./json/fund-name.json');
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
fundDetails.forEach(fund => {
if (fund.portId === portId) {
return res.json(fund);
}
return res.json([]);
});
});
when I hit the url http:localhost:3000/fund-details/1234, I get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (/home/username/Desktop/data-server/node_modules/express/l
ib/response.js:767:10)
It works fine when I don't pass any path param to get all the funds.
Where am I going wrong??
This error is because you are using res.send() multiple time in single api call.
Correct way
if(a){
res.send()
}else{
res.send()
}
Wrong way
if(a){
res.send()
res.send()
}else{
res.send()
}
In your code.
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
fundDetails.forEach(fund => {
if (fund.portId === portId) {
return res.json(fund); // many or single times here
}
return res.json([]); // and here when fund !==portId here
});
});
You can try
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
var flag
var data = []
fundDetails.forEach(fund => {
if (fund.portId === portId) {
flag=true
data.push(fund)
}
});
if(flag){
res.send(data);
}else{
res.send()
}
});
The method res.json(fund) is called per each item in fundDetails and then a further res.json([]) method is called. This leads to your response being send back multiple times (which it shouldn't happen, only 1 response per a single api call should be used).
I suggest that you use an array and push back objects with the matching port id and then send the array back to the user when the operation is completed. To be honest, you don't even need the flag variable to check if funds exists or not since if they don't, you empty data array is sent back.
var data = [];
fundDetails.forEach(fund => {
if (fund.portId === portId)
data.push(fund);
});
res.json(data);

Not able to get the value from node.js backend back to my $http angular ajax call success block.when alerted the value of data it is empty

When posting data from angular passing the data to nodejs server and data is getting successfully inserted in mongodb.
data is requested from angular using $http call to the node.js server but the node.js server inspite of successful fetching of data from database is unable to send the data to the angular js controller.Does res.send(str) mentioned below send back the response with the content to the controller?
In the response used for node.js used str is the data fetched from the mongodb successfully
res.end(str);
In the controller I used
$http.get(url).success(function(data) {
I am not receiving any value for data and it is returning and going to the error block rather than success.
angularjscontroller:
$scope.list = function() {
var url = 'http://192.168.0.104:1212/getangularusers';
$http.get(url).success(function(data) {
$scope.users = data;
}).
error(function(data){
alert('moving into error block');
});
};
$scope.list();
}
appmongodbangulr.js(node.js server)
app.get('/getangularusers', function (req, res) {
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Methods", "GET, POST");
db.things.find('', function(err, users) {
if( err || !users) console.log("No users found");
else
{
res.writeHead(200, {'Content-Type': 'application/json'});
str='[';
users.forEach( function(user) {
str = str + '{ "name" : "' + user.username + '"},' +'\n';
});
str = str.trim();
str = str.substring(0,str.length-1);
str = str + ']';
res.end( str);
}
});
});
app.listen(1212);
Please provide me with the solution how to pass the data that is fetched back to the controller?
You should NOT create JSON manually in any case. You need to use JSON.stringify method.
From the other hand, if you need to convert array of objects into array of other objects, then you definitely need to use map method.
As the result you can see something like the following:
res.writeHead(200, {'Content-Type': 'application/json'});
var usersNames = users.map(function(user){
return user.username;
});
res.end(JSON.stringify(usersNames));
P.S. don't use global scope for names, as you do with str variable.

Node.js: req.params vs req.body

I've been cobbling together code from a few different tutorials to build a basic todo app with the MEAN stack, using node, express, angular, and mongodb. One tutorial covered creating an api for GET, POST, and DELETE actions, but neglected the POST. So, I took it as a challenge to write a function that will update an existing todo. While I got the function working, I encountered an error involving req.params that I didn't understand.
Relevant Code:
Node:
In app.js
app.put('/api/todos/:_id', ngRoutes.update);
which leads to:
exports.update = function(req, res){
var user_id = req.cookies ?
req.cookies.user_id : undefined;
Todo.findByIdAndUpdate(req.params._id,
{ $set: {
updated_at : Date.now(),
content : req.body.formText
}}, function (err, todo) {
if (err)
res.send(err)
Todo.find({ user_id : user_id}, function(err, todos) {
if (err) res.send(err);
res.json(todos);
});
});
};
Angular:
$scope.update = function(id) {
$http.put('/api/todos/' + id, this.todo)
.success(function(data) {
console.log(data);
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Jade/HTML:
form(ng-submit="update(todo._id)")
input.update-form(ng-show="todo.updating" type="text", name="content", ng-model="todo.formText" placeholder="{{todo.content}}")
This function works fine. It updates the todo in question, and returns the entire list to be reloaded onto the page with the updated value.
However, if in the node code, I change
content : req.body.formText
to
content : req.params.formText
I get the following error as my HTTP response:
Object {
message: "Cast to string failed for value "undefined" at path "content"",
name: "CastError",
type: "string",
path: "content" }
Even while, elsewhere in the function,
req.params._id
works fine to retrieve the todo's '_id' property and use it to find the appropriate document in the database. Furthermore, when viewing the request in Firefox's developer tools, the todo object appears in JSON format under the "Params" tab.
Why does this happen? What is the difference between using req.params vs req.body, and why does the second work and the first not?
req.params is for the route parameters, not your form data.
The only param you have in that route is _id:
app.put('/api/todos/:_id', ...)
From the docs:
req.params
This property is an object containing properties mapped to
the named route “parameters”. For example, if you have the route
/user/:name, then the “name” property is available as req.params.name.
This object defaults to {}.
source: http://expressjs.com/en/4x/api.html#req.params
req.body
Contains key-value pairs of data submitted in the request
body. By default, it is undefined, and is populated when you use
body-parsing middleware such as body-parser and multer.
source: http://expressjs.com/en/4x/api.html#req.body
req.params is the part you send in the request url parameter or the header part of requests.
req.params example in postman
In example above req.params is the data we are sending in postman after ninjas in the
url.
route.delete('/ninjas/:id',function(req,res,next)
{
Ninja.findByIdAndRemove({_id:req.params.id}).then(function(ninja)
{
console.log(ninja.toString());
res.send(ninja);
})
.catch(next);
});
req.body is the part you send in body part of requests
req.body example in postman
req.body is the JSON data we are sending in postman so we can access it in the post request body part.
route.post('/ninjas',function(req,res,next)
{
Ninja.create(req.body).then(function(ninja)
{
console.log("POST"+req.body);
res.send(ninja);
})
.catch(next);
});

HTTP POST is submitting empty JSON array

$scope.submitBracket = function(item, event){
console.log("submitting form");
var jsonData = '{"userEmail" : "darwood#mail.com","brackets" :' + JSON.stringify($scope.allGames) + '}';
var testJSON = {"Hello": {"id": "file", "value": "WTF"}};
console.log(jsonData);
var responsePromise = $http.post("/api/brackets", jsonData, {});
responsePromise.success(function(dataFromServer,status,headers,config){
console.log(dataFromServer);
});
responsePromise.error(function(data,status,headers,config){
console.log(data);
});
}
I am working on a MEAN stack app which has a form submitting a JSON object to MongoDB. the JSON is valid. It can be submitted to the database manually with no problems. In the above code example I can see the object being output to the console right before being sent to the db via an HTTP post that is the handed off to an express route. Oddly, I had it working and then in testing removed the records from the db to enter new ones and it started submitting a JSON object with an empty array. Sometimes it throws an "empty response message but the POST request does get a 200 response back. I can't for the life of me figure out why the data is not being passed.
If also tried this alternative function on the Angular side to send the data:
$scope.createBracket = function() {
var jsonData = '{"userEmail" : "darwood#mail.com","brackets" :' + JSON.stringify($scope.allGames) + '}';
$http.post('/api/brackets', jsonData)
.success(function(data) {
$scope.brackets = data;
$location.path('/');
})
.error(function(data) {
console.log('Error: ' + data);
});
};
This is the express route that it is passed off to.
app.post('/api/brackets', function(req, res) {
// create a Bracket
Bracket.create({
text : req.body.text,
done : false
}, function(err, Bracket) {
if (err)
res.send(err);
});
});
when using Postman to test with raw JSON. It hangs for a long time and says that the request is pending. This code was modeled after the tutorial found here
http://scotch.io/tutorials/javascript/creating-a-single-page-todo-app-with-node-and-angular
I have this sample code up and running and inserting into my database with no problem.
Stumped I am. Any help would be appreciated. Thanks.
How did you send the JSON object to MongoDB manually? Was it using command line or with tool like Robomongo?
I would try the following.
No 1. WHAT EXACTLY ARE WE SENDING TO SERVER?
$scope.createBracket = function() {
var jsonData = '{"userEmail" : "darwood#mail.com","brackets" :' +
JSON.stringify($scope.allGames) + '}';
console.info("In Controller-> jsonData = " + JSON.stringify(jsonData)); //**TEST**
//Is jsonData shown as expected?
$http.post('/api/brackets', jsonData)
.success(function(data, status, headers, config) {
$scope.brackets = data;
console.info("Status: " + status);
console.info("Data: " + data);
console.info("Config: " + JSON.stringify(config));
//$location.path('/');
})
.error(function(data) {
console.log('Error: ' + data);
});
No 2. ARE WE GETTING POST REQ DATA AT SERVER?
app.post('/api/brackets', function(req, res) {
console.info("Body: " + JSON.stringify(req.body)); //TEST
console.info("Text: " + JSON.stringify(req.body.text)); //TEST
console.info("Done: " + JSON.stringify(req.body.done)); //TEST
//Have we got the data from the client?
//If so need to check Mongoose Schema/Model MongoDB settings etc
//create a Bracket
//I take it you are using Mongoose and Bracket is the name of the Mongoose Model?
//
Bracket.create({
text : req.body.text,
done : false
}, function(err, bkt) //I would remove Bracket from fn argument as its your Mongoose M
//model name
{
if (err)
res.send(err);
});
});
I had a similar problem using Grails as the backend, so this might not help you. I had to explicitly set the Content-Type.
$http({
method: 'POST',
url: serverURL + '/' + scope.controller + '/save',
data: $form.serializeObject(),
headers: {'Content-Type': 'application/json'}
})
I would have simply commented, but I don't have the reputation to allow that.

Resources