TypeError: Cannot read property '0' of undefined in post method in nodejs - javascript

I am new to nodejs and mongodb.
I am trying to create simple to-do app with nodejs and mongodb.
I have added the task in database.
Now in post method, I am using insertOne method of mongodb and in res.json I am having the following error.
res.json(info.ops[0].data)
TypeError: Cannot read property '0' of undefined
Code :
app.post('/create-item', function(req, res){
db.collection('items').insertOne({ text:req.body.text }, function(err, info){
res.json(info.ops[0])
})
})
Below is the screenshot of Error.

In current versions, there is no property returned named ops when insertOne is successful.
Hence the error TypeError: Cannot read property '0' of undefined
insertOne returns two properties:
acknowledged
Indicates whether this write result was acknowledged. If not, then all other members of this result will be undefined
insertedId
The identifier that was inserted. If the server generated the identifier, this value will be null as the driver does not have access to that data
See:
InsertOne
InsertOneResult
app.post('/create-item', function(req, res){
db.collection('items').insertOne({ text:req.body.text }, function(err, info){
console.log(info.acknowledged)
console.log(info.acknowledged)
res.json(info.acknowledged)
})
})
In previous versions, for example 3.2, different properties were returned for insertOne:
insertOne #3.2
insertOneWriteOpCallback #3.2
Similarly, different properties were returned for updateOne:
updateOne #3.2
updateOneWriteOpCallback #3.2
For more information about migrating to version 4 from earlier versions, see the article:
Changes in 4.x (and how to migrate!)

I encountered the same problem with the to-do app tutorial today. For those interested try changing the line - res.json(info.ops[0])
to - res.json({ _id: info.insertedId.toString(), text: req.body.text })
This gets the inserted id from the database for the newly added item. As "info" doesn't seem to return the inserted text in the current version of mongodb - i added that from the request parameter.
I got my information from here - Get the _id of inserted document in Mongo database in NodeJS

you are doing it wrong way firstly check the err then send the res.json because the error is because your info might be null if data not inserted successfully so you need to do it like
app.post('/create-item', function(req, res){
db.collection('items').insertOne({ text:req.body.text }, function(err, info){
if (err) {
res.json({message: "not inserted successFully"});
return;
}
res.json(info.ops[0])
})
})
now in above code what will happen if data is not inserted successfully it will send the error as response and return from function.

Related

Duplicate object in MongoDB array using Mongoose

I have a Node.js app that is creating a 'unit' in the DB under a 'building' that is saving two 'units' inside the array included in 'building'. Both of these are identical in both data and created timestamp. Anyone have an idea why the below code would be causing this to happen? I am a bit confused at it. I don't see how it would be adding two objects to the array. The code is not being run twice, I checked that with console.log() and just looking at my API logs.
await Building.findOneAndUpdate(
{buildingID},
{"$push": {units: unitData}},
(err, doc) => {
if(err) {
logger.error(`POST unit/new save error for unit: ${unitID} - error message: `, err)
return res.json({success: false, err, message: 'Error saving new unit, please try again'})
}
logger.debug('POST unit/new save() doc: ', doc)
return res.json({success: true, message: `Successfully saved new unit with ID of: ${unitID}`, unitData})
}
)
I ended up figuring it out, when there is an await and a callback it will save the data twice. This is a documented issue on the MongoDB documentation.

Why deleteOne & findById of mongoose work on deleted id

Currently developing an API with nodejs that communicates with a MongoDB database, I noticed a special behavior after deleting a document.
Indeed, my API has several endpoints that allow to retrieve all the animals in the database, to retrieve a specific one using the corresponding id or to delete a specific one, again using the id of the document to delete.
The results I don't understand happen once a document is deleted. Indeed, as you can see in the picture below, when I delete the document of the animal called "Johnny" the queries to find it via its id or to delete it via the same ID continue to work, even if the get returns nothing and the deletion indicates that no operation has been performed.
Personally I expected the same behavior as if I passed a wrong id for a deletion (visible below), but if the id has already been assigned in the database the queries work even after a deletion.
Does MongoDB have a "cache" of deleted documents in order to perform a rollback in case of unwanted deletion?
You will find below the different endpoints that use find, deleteOne & findById
exports.getAllAnimal = (req, res, next) => {
Animal.find().sort({'customer' : 1})
.then(animals => res.status(200).send(animals))
.catch(error => res.status(400).send({ error: error.message }));
};
exports.getOneAnimal = (req, res, next) => {
Animal.findOne({ _id: req.params.id })
.then(animal => res.status(200).send(animal))
.catch(error => res.status(400).send({ error: error.message }));
};
exports.deleteAnimal = (req, res, next) => {
Animal.deleteOne({ _id: req.params.id })
.then(thing => res.status(200).send({ message : 'Animal successfully deleted'}))
.catch(error => res.status(400).send({ error: error.message }));
};
MongoDB does not cache deleted id anywhere.
The thing is that when you said I passed a wrong id for a deletion ... you are passing an id with the same length but not in the required format. That's why Mongoose is throwing you an error.
However, if you follow the id structure of MongoDB to create an id that does not exist in the database and run an operation against it, MongoDB will still return you with success and an empty result.
Try using 5ea08034385a46666b05020f and run the .findById() query function against it. It's going to return you with a success an empty result.
The success only means that the operation is successful, but it doesn't necessarily mean that it actually finds something in the database.
I don't have access to your database, so the id is generated randomly but following the MongoDB ObjectId rules below:
The 12-byte ObjectId value consists of:
a 4-byte timestamp value, representing the ObjectId’s creation, measured in
seconds since the Unix epoch
a 5-byte random value
a 3-byte incrementing counter, initialized to a random value
Generate arbitrary MongoDB ObjectId:
https://observablehq.com/#hugodf/mongodb-objectid-generator

Can I use res.send(foo) twice?

I want to send my token and my user's role to my login.component.ts.
When I was trying to find the problem, in my research I came across someone's suggestion to use
res.write(foo1)
res.write(foo2)
res.end
Instead of
res.send(foo1)
res.send(foo2)
But that didn't work.
I then tried using this to test it:
res.write(foo1)
res.end()
But this is giving me an error:
events.js:174
throw er; // Unhandled 'error' event
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer. Received type object
at write_ (_http_outgoing.js:595:11)
at ServerResponse.write (_http_outgoing.js:567:10)
at User.findOne (C:\Users\notan\GitHub\se3316-notansandwich-lab5\server\controllers\user.controller.js:46:33)
at C:\Users\notan\node_modules\mongoose\lib\model.js:4604:16
at C:\Users\notan\node_modules\mongoose\lib\query.js:4348:12
at process.nextTick (C:\Users\notan\node_modules\mongoose\lib\query.js:2850:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at C:\Users\notan\node_modules\mongoose\lib\model.js:4606:13
at C:\Users\notan\node_modules\mongoose\lib\query.js:4348:12
at process.nextTick (C:\Users\notan\node_modules\mongoose\lib\query.js:2850:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
This is my user.controller.js, which I use in my route.js which is used in my sever.js
const User = require('../models/user.model')
const jwt = require('jsonwebtoken')
exports.user_create = function (req, res, next) {
let user = new User(
{
_id: Math.random().toString(36).substr(2, 5), // generate a random ID
email: req.body.email,
password: req.body.password,
firstName: req.body.firstName,
lastName: req.body.lastName,
role: "user"
}
);
user.save(function (err, registeredUser) {
if (err) {
return next(err);
}
else {
let payload = { subject: registeredUser._id}
let token = jwt.sign(payload, 'secretKey')
res.status(200).send({token})
}
})
}
exports.user_login = function (req, res, next) {
let userData = req.body
User.findOne({email: userData.email}, (err, user) => {
if (err) {
console.log(err)
}
else {
if (!user) {
res.status(401).send('Invalid email')
}
else if (user.password !== userData.password) {
res.status(401).send('Invalid password')
}
else {
let payload = {subject: user._id}
let token = jwt.sign(payload, 'secretKey')
//res.status(200).send({token})
res.status(200).write({token})
//let role = this.user.role
// res.status(200).write({role})
res.end()
}
}
})
}
Using this works
res.status(200).send({token})
But this does not
res.status(200).write({token})
res.end()
In response to the title of your question:
Can I use res.send(foo) twice?
No, you cannot call that twice for the same request.
See the second part of the answer since the OP changed their question after I wrote this first part
In Express, you can only use res.send() or res.json() or res.end() once per request. When you execute those, it sends the request. If you try to send more on the same request, it will do nothing and will show a warning in Express.
res.write() can be called more than once, then followed by res.end() when you are finally done with the request.
In your example:
res.status(200).send({token})
res.end()
The res.send() already calls .end() for you so trying to call it again is considered an error because the request has already been sent.
FYI, .status(200) is not necessary. The default status is already 200 so res.send({token}) will already have a 200 status.
More Recent Answer for the Modified Question
Now that you've completely changed the question to be about res.write({token}), that does not work because res.write() requires a String or a Buffer as an argument and you were giving it an object. You would have to manually convert the object to JSON yourself:
res.type('application/json');
res.write(JSON.stringify({token}));
res.end();
And note that this also sets the appropriate content type. If your object is large with res.write() you may also have to pay attention to the write buffer being full and listen for the drain event. res.write() is a much lower level facility (it's at the http level, not at the Express level) than the Express functions for sending data.
Built into Express, you can use res.send() or res.json() which are Express methods that will both that you passed an object, automatically convert it to JSON for you and set the content type to JSON also. It will also handle any buffer full issues in the write stream too.
res.send({token});
or, I prefer to be more explicit in my Express code with:
res.json({token});
And, if you're trying to send multiple pieces of data, you can put them into the same object:
res.json({token, role});
Calling res.status(200).send({ token }) ends the request and sends the object back to the client...
Since the request is now ended... Calling res.end() generates the error...
You'd usually use res.end if u were piping some data (usually binary) after several res.write to close the pipe...
For more info... checkout Express' docs in the response object...
https://expressjs.com/en/api.html#res
Also... U can't send an object using res.write...
From your error... it says that it inly accepts a string or buffer. If u want to send plain objects... res.send or res.json would be more appropriate...
I found the solution: I can send multiple things in an res.send(...)
res.status(200).send({token, role})
If you want to use res.write(argument) you have to pass the argument as a string or Buffer, just like the error message says. To get the same effect just convert your response object to a string:
res.status(200).write(JSON.stringify({token}))

How to update record in mongoose with nodejs?

I am trying to update a record in mlab, a remote mongodb database. I am using express, mongoose and nodejs to update a certain record. I also use promises to avoid the callback pyramid.
Api file:
router.put('/chairs/:id', function(req, res, next){
Chair.findByIdAndUpdate({id:req.params.id}, req.body)
.then(function() {
Chair.findOne({id:req.params.id})
})
.then(function(chair){
res.send(chair);
})
.catch(next);
});
I tested the code above using Postman 'x-www-form-urlencoded'. When i try to UPDATE just 1 field 'title' and send PUT REQUEST , I am prompted with
{
"myError": "Cast to ObjectId failed for value \"{ id: '5953532df36d28458af5609f' }\" at path \"_id\" for model \"chair\""
}
I want to update a record and get the ID again and print the updated record.
Please help. I am not sure how to properly nest my promises

node.js mongojs findOne callback returning error as null

Presently being driven up the wall by this error.
I am running a node.js app with the mongojs wrapper for mongodb. I Started mongod on the default port, then ran
var db = require('mongojs').connect('localhost:27017/my_db');
var users = db.collection('users');
users.findOne({'fb_id' : fbUserMetadata.id}, function(err, user) {
console.log(err);
console.log(user);
debugger;
});
however err and user are both 'null'. To my knowledge, err should be populated with some kind of data even if it doesn't find anything.
How do I get the callback function to work properly? Pardon the newbie question.
When the findOne query doesn't find at least one matching document, the second parameter of the callback (in this case user) is set to null. It's not an error, so err is also null. So what you're seeing is the expected no-match-found response.
Update
Note that findOne has been deprecated in the 2.0 driver, but its replacement also exhibits this same behavior:
users.find({'fb_id' : fbUserMetadata.id}).limit(1).next(err, doc) {
// doc is null if a matching document wasn't found
});

Categories