mongoose, nodejs how to add items into mongo array - node.js

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.

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.

How to access data on server side from AJAX post request

I have tried reading a lot of posts on this but none have really helped me. I don't get how to access the data I am sending from a post request. For context, I have a .ejs html file where I am making an asynchronous post request to the server. This is the code for that:
$("#commentButton" + thisPostId).click(function () {
$.ajax({
type: "POST",
url: "/comment",
data: {
comment: $("#commentInput" + thisPostId).val(),
postId: $("#postIdInput" + thisPostId).val()
},
success: function (data) {
if (data.success) {
let asyncComment = "<p>whatsupbro</p>";
$("#li" + thisPostId).append(asyncComment);
} else {
// ADD BETTER ERROR IMPL
alert('error');
}
}
});
On the server side, I want to retrieve the arguments in "data". This is what I have so far. I have tried so many things, like doing req.data.comment, req.comment, etc. Below is the start of my node.js function that is supposed to get the request and do some stuff with it. What matters is I want the comment in commentInfo and postId in commentInfo to be what I am sending in the post request as "comment" and "postId". I really am just not sure how to access this data (req.body.mycomment doesn't work either).
var createComment = function(req, res) {
var commentInfo = {
comment: req.body.myComment,
username: req.session.username,
commentId: new Date().getTime(),
postId: req.body.postId
};
console.log(req['comment']);
Thanks for the help. If there is anything else I should add let me know.
You need appropriate middleware to actually read the body of the POST request and to then parse it and place the contents into req.body.
Assuming you are using Express, then you need one of these placed before the route that receives the POST:
app.use(express.json()); // for application/json content-type
app.use(express.urlencoded()); // for application/x-www-form-urlencoded content-type

express-validator converting body to [object Object]

I'm trying to escape form input for a simple demo express app, and I'm getting a result I don't understand.
When I execute this route:
/* Update a toy */
app.post('/toys/:id', [
body().escape()
], (req, res) => {
console.log("Update: ");
console.log(req.body);
toyController.update(req, res);
});
I get this output:
Update:
[object Object]
It looks to me as if the request body is getting destroyed.
If I remove the middleware,
/* Update a toy */
app.post('/toys/:id', (req, res) => {
console.log("Update: ");
console.log(req.body);
toyController.update(req, res);
});
I get the expected result:
Update:
{
toy: {
name: 'Playstation 4',
description: 'A gaming console',
manufacturer: 'Sony',
price: '400'
},
commit: 'Update Toy'
}
Update: If I call body('toy') instead of body(), I get the following result:
{ toy: '[object Object]', commit: 'Update Toy' }
So, it appears that the problem lies in the fact that that body is a nested object.
How can I apply the validation/sanitization to body.toy instead of all of body?
Is there a way that I can directly call the escape code an apply it to a specific string, instead of using the entire middleware setup?
You need to use body().escape() in a correct way. Please refer below an example.
const express = require('express');
const { body } = require('express-
validator');
const app = express();
app.use(express.json());
app.post('/comment', [
body('email')
.isEmail()
.normalizeEmail(),
body('text')
.not().isEmpty()
.trim()
.escape(),
body('notifyOnReply').toBoolean()
], (req, res) => {
// Handle the request somehow
});
Above example contains the right way to use express validator.In the example above, we are validating email and text fields, so we may take advantage of the same chain to apply some sanitization, like e-mail normalization (normalizeEmail) and trimming (trim)/HTML escaping (escape).
The notifyOnReply field isn't validated, but it can still make use of the same check function to convert it to a JavaScript boolean.
Sorry the indentation is not good, I am posting the answer from mobile.
Since you modified the question. You can use schema validation by express- validator.
https://express-validator.github.io/docs/schema-validation.html

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.

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

Resources