MongoDb update a route with two id's? - node.js

My models on the front end are not saving. :tid is the team id and :pid is player id.
router.put('/api/players/:tid/:pid', player.update);
update: function(req, res) {
models.Player.update({ _id: req.params.pid }, function(err, player) {
if (err) {
res.json({error: 'Player not found.'});
} else {
console.log(player);
}
})
} // obviously looks a bit illogical, but not sure what to do here 100%
Right now I am just using a static team ID in my backbone collection.
var Backbone = require('backbone'),
PlayerModel = require('../models/player');
module.exports = PlayersCollection = Backbone.Collection.extend({
model: PlayerModel,
url: '/api/players/545d1d72f7895d00008e2f43'
});
So basically that grabs the players with team_id 545d1d72f7895d00008e2f43 but it also wants to post to that route with the players id, which is why my route above requires, :tid/:pid
I am just not sure what my mongoDB update function should look like to properly update the model on the server.

Okay I forgot this was an old gotcha, I had ran into this a while back. I had to add req.body.
update: function(req, res) {
models.Player.update({ _id: req.params.pid }, req.body, function(err, player) {
if (err) {
res.json({error: 'Player not found.'});
} else {
res.json(player);
}
});
}

Related

NodeJS & MongoDB - Get Objects based on array of ids

I am having trouble returning getting the Objects in a collection based on an array of ObjectIds.
I have a collection of 10 items. In an iOS app, I am passing up an array of ObjectIds that user has saved as favorites to return just those two in a favorites tab, for example -
["59f85eae4545XXX9b94d53d3", "59f85eae45454XXXb94d76a1"]
For now, I have just hardcoded the ObjectIds at the top of the request using NodeJS just until I get the request working, below is the request -
exports.list_favs = function(req, res) {
Articles.find({
_id: { $in: [
mongoose.Types.ObjectId('59f85aXXXXf1769b94d53ce'),
mongoose.Types.ObjectId('59f85eaeXXXX1769b94d53d3')
]}
}, function(err, articles) {
if (err)
res.send(err);
res.json({
data: {
articles
}
});
});
};
And I have even tried this -
exports.list_favs = function(req, res) {
var ids = new Array( new ObjectId("59f85eaeXXXX1769b94d53d3"), new
ObjectId("59f85aXXXXf1769b94d53ce") );
Articles.find({
_id: { $in: ids}
}, function(err, articles) {
if (err)
res.send(err);
res.json({
data: {
articles
}
});
});
};
Both these give me an error "CastError"
"Cast to ObjectId failed for value \"favourites\" at path \"_id\" for model \"Articles\""
This is how it looks in the database so I am completely baffled as to what I am doing wrong. Image of ObjectId in database
I've followed multiple other answers on StackOverflow with no luck.
Gist to full project code
Do this way :
exports.list_favs = function(req, res) {
Articles.find({
_id: {
$in: [mongoose.Schema.ObjectId('59f85aXXXXf1769b94d53ce'),
mongoose.Schema.ObjectId('59f85eaeXXXX1769b94d53d3')]
}
},
function(err, articles) {
if (err)
res.send(err);
else
res.json({
data: {
articles
}
});
});
};

RESTful API singular route with a single object to retrive and update (options parameters)

Hi i'm stucked trying to create a route in the RESTful API server in express.
I've configured other routes and now i need to configure an ('/options) or ('/profile') singular route where there is only one object to retrive and update.
Basically i need to do the same of the json-server module in the Singular routes section.
So when i visit the /options endpoint i got the predefined object with this schema
{
tax: Number,
inps: Number,
ritenuta: Number,
banca: {
nome: String,
iban: String
}
}
to update.
Here's my actual routes for /options:
var Option = require('../models/option');
var express = require('express');
var router = express.Router();
router.route('/options')
.get(function(req, res) {
Option.find(function(err, options) {
if (err) {
return res.send(err);
}
res.json(options);
});
})
.post(function(req, res) {
var option = new Option(req.body);
option.save(function(err) {
if (err) {
return res.send(err);
}
res.send({message: 'Option Added'});
});
});
// Save an option
router.route('/options/:id').put(function(req, res) {
Option.findOne({ _id: req.params.id}, function(err, option) {
if (err) {
return res.send(err);
}
for (prop in req.body) {
option[prop] = req.body[prop];
}
option.save(function(err) {
if (error) {
return res.send(err);
}
res.json({message: 'Option updated!'})
});
});
});
// Retrive an option
router.route('/options/:id').get(function(req, res) {
Option.findOne({ _id: req.params.id }, function(err, option) {
if (err) {
return res.send(error);
}
res.json(option);
});
});
// Delete an option
router.route('/options/:id').delete(function(req, res) {
Option.remove({ _id: req.params.id}, function(err, option) {
if (err) {
return res.send(err);
}
res.json({message: 'Option deleted!'});
});
});
module.exports = router;
but it's much complicated. It should be simpler. In fact, in this case i need to get all the options, get the id of options[0] and make a call with the id as params to retrive the object and update.
Any suggestions please?

Am I doing this API Put request correct with mongoose?

So I'm using mongoose and mongodb and express routing to do it.
I have a user document with an empty array on creation- "todolist", I want to be able to edit this to add a task to the list as well as edit those tasks.
Right now I'm thinking the best way to do it is have the server check if the req.body has certain variables set to know which part of the document I need to edit.
Is this the correct approach or am I suppose to create a new API route for something like this or am I doing it correctly by checking req variables from the client to determine what I want to edit on the server? Is there a cleaner way to do this too?
router.put('/:user_id', function(req, res) {
User.findById(req.params.user_id, function(err, user){
if(err){
res.send(err);
return err;
}
if(req.body.createTask) {
user.todolist.push({
"task_name": req.body.task_name,
"task_importance": req.body.importance,
"task_completed": false
})
}else if(req.body.edit_task){
if(req.body.edit_task_name) {
user.todolist[req.body.index].task_name = req.body.new_task_name;
}else if(req.body.edit_task_importance) {
user.todolist[req.body.index].task_importance = req.body.new_task_importance;
}else if(req.body.edit_task_completed) {
user.todolist[req.body.index].task_completed = true;
}
}
user.save(function(err){
if(err)
res.send(err);
res.json({message: 'User updated'});
})
})
});
I would recommend you to model your database with proper entities, creating a Task model separately from User and adding a reference to User, like this:
var TaskSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
name: String,
importance: String,
completed: Boolean
});
If you want to, you can also add reference to Task on User:
var User = new mongoose.Schema({
// note this is an array
todolist: [{
type: mongoose.Schema.ObjectId,
ref: 'Task'
}]
});
Doing that, to make things simpler, you should create a pre save hook to automatically add tasks to user:
TaskSchema.pre('save', function(next) {
if (this.isModified('user') && this.user) {
// $addToSet will add this document to the user's todolist (if it's not already there)
User.findOneAndUpdate({_id: this.user}, {$addToSet: {todolist: this}}, function(err, doc) {
next(err);
})
} else {
next();
}
});
This way the correct approach would be provide endpoints correlated to entities. So, to create a task, a user should request POST /tasks with body { user: '123', name: 'name', ... }:
router.post('/tasks', function(req, res) {
Task.create(req.body, function(err, task) {
if (err) return res.status(500).send(err);
return res.status(201).json(task);
});
});
It will automatically add the task to the user 123 with our pre save hook.
To edit the task, just make that on task endpoint:
router.put('/tasks/:id', function(req, res) {
Task.findById(req.params.id, function(err, task) {
if (err) return res.status(500).send(err);
if (!task) return res.status(404).send('Not Found');
task.name = req.body.name || task.name;
task.importance = req.body.importance || task.importance;
task.completed = true;
task.save(function(err, task) {
if (err) return res.status(500).send(err);
res.status(200).json(task);
});
});
});
You don't need to edit user as it only keeps tasks reference.
If you want to get users with tasks data populated (and not only their ids), just do:
router.get('/users', function(req, res) {
User.find({}).populate('todolist').exec(function(err, users) {
...
});
});
For queries like that above, I recommend you to use some library like querymen, which parses querystrings (e.g. GET /users?q=Some+Search&limit=10&page=2) to MongoDB query arguments.
Hope it helps. :)

MongoDB objectID stranger Error

Using a MEAN Stack deployment on Heroku I am able to GET and DELETE Documents with mongoDB's findOne and deleteOne functions. However when I try to PUT a document with the mongoDB updateOne/update function, I receive this error (server side) :
The _id field cannot be changed from {_id: ObjectId('56d4d71191fdc81100974d0b')} to {_id: "56d4d71191fdc81100974d0b"}.
Seems strange because I am using the same method in my server code for updateOne as in findOne (again, findOne works fine):
app.get("/contacts/:id", function(req, res) {
db.collection(CONTACTS_COLLECTION).findOne({ _id: new ObjectID(req.params.id) }, function(err, doc) {
if (err) {
handleError(err.message, "Failed to get contact");
} else {
res.status(200).json(doc);
}
});
});
app.put("/contacts/:id", function(req, res) {
var updateDoc = req.body;
db.collection(CONTACTS_COLLECTION).updateOne({_id: new ObjectID(req.params.id)}, updateDoc, function(err, doc) {
if (err) {
handleError(err.message, "Failed to update contact");
} else {
res.status(204).end();
}
});
});
Any suggestions?
I think you have problem at var updateDoc = req.body
As req.body contains id field and you are searching from object to update by that id, mongodb thinks you are trying to update
id field too which is not allowed.
One solution is to remove id field from your updateDoc object.
e.g.
delete updateDoc._id;
now try again and see if it works.
Your final function should look like
app.put("/contacts/:id", function(req, res) {
var updateDoc = req.body;
delete updateDoc.id;
db.collection(CONTACTS_COLLECTION).updateOne({_id: new ObjectID(req.params.id)}, updateDoc, function(err, doc) {
if (err) {
handleError(err.message, "Failed to update contact");
} else {
res.status(204).end();
}
});
});

After form submission, new data does not appear without page reload

In my ExpressJS app I have two routes to the same location, one for 'get' and one for 'post'.
On the 'get' page it dumps all the documents from my MongoDB collection, via MongooseJS, followed by a form to add a new record to the collection.
On the 'post' page it takes in the form data and adds it to the collection, and then displays the same page you see via 'get'.
It works, but after the form is submitted the new record doesn't appear unless I reload the page. I put the code that renders the page at the very bottom, below the part that adds the data to the collection so in my mind that should work but it doesn't.
exports.index = function(req, res){
Server.find({},
function(err, docs) {
if (!err){
res.render('servers', { title: 'verify', results: docs});
}
else { console.log(err);}
}
);
}
exports.add = function(req, res){
newServer = new Server({
name: req.body.name,
os: req.body.os,
osVersion: req.body.osVersion
});
newServer.save(function (err) {
if (err) {
console.log(err);
}
});
Server.find({},
function(err, docs) {
if (!err){
res.render('servers', { title: 'verify', results: docs});
}
else { console.log(err);}
}
);
}
Ok, I seem to make this mistake over an over again with callbacks. I fixed the problem.
exports.add = function(req, res){
newServer = new Server({
name: req.body.name,
os: req.body.os,
osVersion: req.body.osVersion
});
newServer.save(function (err) {
if (err) {
console.log(err);
} else {
Server.find({},
function(err, docs) {
if (!err){
res.render('servers', { title: 'verify', results: docs});
}
else { console.log(err);}
}
);
}
});
}
Yes, those DB queries are async, so callbacks will solve this issue. However, you should look into using promises. Mongoose returns promises by default or you could import your library of choice. They will come in handy when dealing with nested callbacks and queries and also Error handling.

Resources