Node.js: req.params vs req.body - node.js

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);
});

Related

Express is not reading the request body

I am new in node and express. So, I was learning how to do post request. When I learned, it was alright and it was creating another document in mangodb database. Today when I tried to do the same, this problem occurred.
I tried different things like removing required part in schema. But it seems express is failing to read req.body since when I send the request without any required fields in schema, it happily accepted and filled up default fields. I don't really understand what is happening. I was parallelly doing another project which is not giving this error.
PS. I am using mongoose#5.
Code to create tour:
exports.createTour = async (req, res) => {
try {
const newTour = await Tour.create(req.body);
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message,
});
}
};
If the content-type for your POST is JSON, then you need this:
app.use(express.json())
as middleware before your POST request handler. That will read the JSON body of the request, parse it and put it in req.body.

request body is undefined when trying to POST in express

For some reason, request body is undefined when trying to make a post request:
here is my router:
router.route("/").post(schoolController.createSchool);
here is what I put in schoolController for createSchool:
exports.createSchool = async (req, res) => {
try {
console.log(req.body);
// return undefined
const newSchool = await School.create(req.body);
res.status(201).json({
status: "success",
data: {
school: newSchool,
},
});
} catch (err) {
res.status(400).json({
status: "fail",
message: err,
});
}
};
adding on, I am following jonas's nodejs course on udemy, and he has almost the exact thing as this, except its for handling tour requests instead of school
The problem you are facing here is likely that you have not configured the body-parser middleware correctly. The normal req will not contain any property by the name body. Only once the request passes through the body-parser middleware, the body key will be added to the req. You can try console logging req. If the request is logged correctly, it is more than likely that you need to look into configuring you bodyparser middleware correctly before you can use req.body in your code.

mongoose, nodejs how to add items into mongo array

I have a collection like
{
id:"david123",
friends[{id:joe321, lname"woo", fname"joe"}]
}
i want to add new elements into friends
i currently have this, but it does not seem to be working
app.post('/users/:uid/friends', function(req, res){
var userId = req.params.uid;
Friend.update({'_id': userId},
{$push: {Friends: req.body.friend}},
{ upsert : true },
function(err, result){
if (err){
console.log(err);
} else {
res.status(200).json(result);
}
})
});
i defined my schema like this
var FriendSchema = new mongoose.Schema({
_id: String,
Friends: [{
_id: String,
fname: String,
lname: String
}]
});
when i make a request i send
{ friend: '{userId:"john123",lname"smoth",fname"john"}',
userId: 'userId123' } and im getting
[TypeError: Cannot use 'in' operator to search for '_id' in {userId:"john123",lname"smoth",fname"john"}]
The sentence "it does not seem to be working" tells us nothing, really. Is there any error printed? Is there bad data inserted? No data? What is the response to the HTTP request? Those are the most important questions but since no relevant info is provided I can only give you some hints:
Make sure that the connection to Mongo is successful
Make sure that you're connecting to the database that you think you are
Make sure you use body parser with correct encoding if needed
Make sure to use $addToSet instead of $push to avoid duplicates
Make sure to return a response on error and not only on success
Make sure you send a request with POST method with JSON content type
Make sure that you send the data in the request body
Make sure that the JSON in your request contains the friend property
Make sure you have some request logging
For (3) see: https://github.com/expressjs/body-parser#bodyparserjsonoptions
For (4) see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/
You didn't say anything about a framework that you use and you did not your question with a framework tag but your code suggests that you may be using Express. If that's the case then to use req.body in the request handlers you need to use a correct body parser middleware:
npm install body-parser --save
and in your code - at the beginning:
const bodyParser = require('body-parser');
and somewhere after you have app but before app.post(...); you need:
app.use(bodyParser.json());
if you want to have the request body parsed as JSON so you could use req.body and req.body.friend in your handler.
And use some basic logging in your request handler:
app.post('/users/:uid/friends', (req, res) => {
// start with:
console.log('Request body:' JSON.stringify(req.body));
// the rest of the logic ...
});
to see what is actually passed in the response and that it is correctly deserialized by the appropriate body parser.

KeystoneJS CloudinaryImage upload via API

There seems to be lack of documentation on this topic. I'm trying to upload an image and set it to avatar: { type: Types.CloudinaryImage } in my Keystone model.
I'm posting content as multipart form data with the following structure: avatar: <raw_data>. Here is how I handle this in my API:
exports.upload_avatar = function(req, res) {
if (!req.files.avatar) {
console.info('Request body missing');
return res.status(400).json({ message: 'Request body missing', code: 20 });
}
req.current_user.avatar = req.files.avatar;
req.current_user.save();
}
where current_user is a mongoose model. What I find confusing is how to set my CloudinaryImage type field to the data I receive in the API.
So, rather than just setting the avatar to the raw data (which would work fine for e.g. a string field), you'll need to go through the update handler, which calls to the {path}_upload special path in cloudinary image.
You should then be able to do avatar.getUpdateHandler, perhaps following this example.
I would like to share what worked for me. The process is kind of strange but by adding in this code, all of the model validation works just fine and cloudinary uploads are set.
post(req, res, next) {
const newBundle = new Bundle(); //A mongoose model
newBundle.getUpdateHandler(req).process(req.body, (err) => {
if (err) {
return res.status(500).json({
error: err.message,
});
}
return res.json(newBundle);
});
}
When posting to the endpoint, all you need to do is make sure you set your file fields to be {databaseFieldName}_upload.
Ok after some digging through the source code, I figured out a way to do that:
exports.upload_avatar = function(req, res) {
req.current_user.getUpdateHandler(req).process(req.files, {fields: 'avatar'}, function(err) {
if (err) {
return res.status(500).json({ message: err.message || '', code: 10 });
}
res.send('');
});
}
I had the following gotchas:
use getUpdateHandler to update CloudinaryImage field.
use "magic" naming for multipart form data fields you POST to your API: {field_name}_upload, which in my case would be avatar_upload.
process req.files, which is a dictionary with your field names as keys and your file data as values. req.body is empty due to some post-processing with multer.
invoke update handler on your keystone model (you need to fetch it with find first) rather than on a specific field. Then specify {fields: <>} to limit its scope, otherwise you could have some issues like validation errors trying to update the whole object.

How can I pass an array to the server when using $http?

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",...]

Resources