I have a NodeJS backend call with MongoDB. This particular api call was working fine for a long time, and when testing out something on the frontend, I realized the call was not going through. I've checked the routes, controller, index files, and tested the call through Postman which works fine, no errors, and even returns an ObjectId (which means it must be interacting with the database, right?). However, when I search the mongo shell nothing comes back, which tells me it is not saving. I cannot find anything wrong and get no errors anywhere along the way. I checked on MongoDB Atlas, the collection only has 4kb of data so it is not that it is 'too full' and have tested all the other api calls (get, patch, delete) which work fine and have no issues like this in my other collections.
Even weirder, during the save call I push the ID to 2 other collection's documents as a ref. The Mongo shell does show that the new Id is populating to the other documents, but yet it is not saving the actual document... which should be happening prior to the push and needs to happen in order to get the ObjectId
Below is the controller for adding a new Visit document, the route for it, and the response from postman. I really have no idea of how else to determine what is taking place.
Controller
exports.addVisit = async (req, res) => {
const hoursValue = getTotalHours(req.body.visitStart, req.body.visitEnd)
try {
const visit = new Visit({
totalHours: hoursValue,
user: req.body.user,
client: req.body.client,
visitStart: req.body.visitStart,
visitEnd: req.body.visitEnd,
location: req.body.location
});
const user = await User.findById(req.body.user);
const client = await Client.findById(req.body.client);
visit.user = user._id
visit.client = client._id
visit.save();
user.visits.push(visit._id);
user.save();
client.visits.push(visit._id);
client.save();
console.log(visit)
console.log(user)
console.log(client)
res.status(201).send(visit);
} catch (error) {
res.status(400)
res.send({ error: "Error adding visit", error})
}
}
Route
router.route("/visits").post(addVisit)
Postman call to: http://localhost:5000/visitapi/visits
{
"client": "6205a8313fe12d6b4ec354c4",
"location": "Home",
"user": "62410a1dcaac9a3d0528de7a",
"visitStart": "2022-10-12T17:00:00.000Z",
"visitEnd": "2022-10-12T19:00:11.000Z"
}
Postman response
{
"client": "6205a8313fe12d6b4ec354c4",
"user": "62410a1dcaac9a3d0528de7a",
"location": "Home",
"visitStart": "2022-10-12T17:00:00.000Z",
"visitEnd": "2022-10-12T19:00:11.000Z",
"totalHours": 2,
"goals": [],
"id": "635302bb48e85ff6ad17ee59"
}
NodeJs console logging the same new document with no errors:
{
client: new ObjectId("6205a8313fe12d6b4ec354c4"),
user: new ObjectId("62410a1dcaac9a3d0528de7a"),
location: 'Home',
visitStart: 2022-10-12T17:00:00.000Z,
visitEnd: 2022-10-12T19:00:11.000Z,
totalHours: 2,
_id: new ObjectId("635302bb48e85ff6ad17ee59"),
goals: []
}
MongoShell showing the Client collection document stored the new Visit document Id:
visits: [
ObjectId("6257158d157e807e51c7e009"),
ObjectId("62fd852a252b83f4bc8f9782"),
ObjectId("63056cee252b83f4bc8f97e9"),
ObjectId("634ee01ec582da494032c73e"),
ObjectId("634ee09cc582da494032c7aa"),
ObjectId("634ee3d6ddbe3f7e6641d69e"),
ObjectId("634efcf1ddbe3f7e6641d6f9"),
ObjectId("634efdd3ddbe3f7e6641d71b"),
ObjectId("635029937da8972360d907c1"),
ObjectId("6350a0e37da8972360d9084f"),
ObjectId("635302bb48e85ff6ad17ee59") //_id matches the same returned by Postman/ NodeJS
],
Again, everything goes through with no errors on postman or the front or back end, and the backend even logs the returns the new document but no new document is being saved to the DB unless I manually pass it in the Mongosh shell, and only for this one collection. Totally lost, I'd appreciate any guidance on how to explore/ resolve this. Thanks
Edited to include the solution based on the discussion in comment
The problem could be in the mongoose schema. If the property names do not match, mongoose will simply ignore the mismatched properties.
Previous answer
Those mongodb calls are expected to be async. You might want to add await to all those.
visit.user = user._id
visit.client = client._id
await visit.save(); // <- this
user.visits.push(visit._id);
await user.save(); // <- this
client.visits.push(visit._id);
await client.save(); // <- and this
Related
I am learning by building. I am building a blog management system using Reactjs, Nodejs, Mongodb.
I would like to store some frontend values in the database so that anyone I give admin permission can post, edit and delete such values. These values are website name, sub-name, sidebar bio description, header image and bio image.
This is the code to create the value:
//create new frontend paramters into database
router.post("/", async (req, res) =>{
const newFrontendValues = new Frontend(req.body);//we called the frontend model we created and we used req.body
try{
const savedFrontendValues = await newFrontendValues.save()//we tried to save the frontend values created
res.status(200).json(savedFrontendValues)
}catch(err){
res.status(500).json(err)
}
});
I wrote the code in node to get the values like this after creating them:
//get frontend parameters
router.get("/:id", async (req, res) =>{
try{
const frontend = await Frontend.findById(req.params.id)
res.status(200).json(frontend)
}catch(err){
res.status(500).json(err)
}
})
my server api code
app.use("/api/frontend", frontend)
In react, I wanted to call the _id of the values but I am lost. I really don't know how to go about that.
It is working fine as desired in postman because I can directly implement the value _id.
See attached image
But in React, I wanted that to be dynamic.
Here is my React code:
useEffect(() => {
const fetchFrontendValue = async () =>{
const res = await axios.get("/frontend")
console.log(res.data)
}
fetchFrontendValue()
}, [])
How do I add the _id to this
axios.get("/frontend")
You'd want to look at get request parameters. Usually as a convention, people pass them in the URL. So it would be something like http://localhost:5000/api/frontend?id=617944dc7e00022337a483be78 and on the API side, you'd use req.body.id to pass that to the database. There are other ways to do it too, but this is the most common because some old browser drop the parameters attached to a GET request. Here's a link:https://www.w3schools.com/tags/ref_httpmethods.asp
You should consider going for a complete solution. On a basic level, you should be following these steps
Implement a backend route /getall that fetch out all items in DB in this manner
await Frontend.find({})
Render the fetched list on frontend side in a way that each item would be a React UI item and as part of each item, you have the buttons for deleting and updating the item data
{backendData?.map((item, index)=><SingleItem key={item?._id} data={item} />)}
As each SingleItem has update and delete buttona and also the mongodb ID as part of data, so on clicking update and delete button, you will get the id from the data and call relevant DB Url on backend side
How best can I target all posts associated with a user using the user's objectId? I am using mongoose and node.js.
I currently have this code:
router.get("/", async(req, res) => {
const username = req.query.user; // req.query is inbuilt. The last word .user is the keyword that will be used to query your logic
const catName = req.query.cat;
try {
let posts;
if(username) {
posts = await Post.find({username: username})
}
else if(catName) {
posts = await Post.find({categories:{
$in:[catName]
}})
}
else {
posts = await Post.find()
}
res.status(200).json(posts)
} catch(err) {
res.status(500).json(err)
}
})
It's not working for me. I want to get all the posts made by a user once you click on the user's name. username won't work for me because I used .populate() method from mongoose to ref username to my User Model from Post Model. So, username is now an objectId.
How do I query this using their objectId?
For anyone who may experience this challenge, especially those of us still learning. How I solved my problem:
Since username is now a referenced objectId due to use of .populate() method, I was using the username in postman like this localhost:5000/api/posts?user/dave
'dave' is the username of the user. This didn't work because mongoose was waiting for the objectId to fetch the user. So, after hours of search, it came to my mind to use the objectId instead of the username and it worked. I was able to fetch only the posts made by that objectId.
I had to do same thing in my React.js. My react route query looks like this:
<Link to={`/?user=${singleItem.username._id}`} className="link">
Everything is working fine, now.
Sorry if this turns out for me to be missing something really obvious, but I've been running into issues with this code for some time and I"m just not getting it. I'm currently trying to work on an authentication system for an app I'm developing for school, and to do that I'm making an API endpoint that takes an inputted password, hashes it, then compares it the stored hashed password to see if the two match. My current code looks like this:
router.route('/auth/').post((req, res) => {
const userName = req.body.userName;
const password = User.generateHash(req.body.password);
let query = User.findOne({"userName": userName},{_id:0, userType:0, fName:0, lName:0, password:1, email:0, userName:0, __v:0});
let serverpass = query.get("password");
if (serverpass == password) {
res.status(200).json("User Authenticated!");
} else {
res.status(400).json("Passwords do not match." + password + " " + serverpass)
}
})
Based on what I read, the findOne() method returns a non-cursorable document you're capable of getting values from using the .get() method. However, whenever I test it using a REST client the serverpass variable always comes back as an object of type Object and the get() method always returns undefined. Any help on this would be appreciated. For reference, here is the database entry I'm trying to read:
{
"_id": *REDACTED*,
"userType": "AuthTest",
"fName": "Auth",
"lName": "Test",
"password": *REDACTED*,
"email": "new#new.com",
"userName": "AuthTest",
"__v": 0
},
where the password is just "password" hashed. The supplied information to the REST client is:
{
"userName":"AuthTest",
"password":"password"
}
Thanks!
MongoDB queries are asynchronous, this means you need to handle the relative Promise. This can be easily achieved with async/await operators.
router.route('your-path').post(async (req, res) => {
// Get your parameters...
try {
const user = await User.findOne(...);
// TODO: implement your business logic...
} catch (e) {
// TODO: error handling...
}
});
I'm trying to implement a Password reset. So I'm taking the phone number of the user, getting the document from the database using the phone number to find it, and I'm taking the new password and trying to update the corresponding document using a PUT request in my Cloudant database.
app.post('/pass_rst', function(req,response){
var log='';
//log is just for me to see what's happening
var phone= req.body.phone;
log+=phone+'\n';
db.find({selector:{'phone':phone}}, function(err, result){
if(err){
throw err;
}
if(result.docs.length==0){
response.send('User doesnt exist');
}else{
var existing_data=result.docs[0];
log+=JSON.stringify(existing_data)+'\n';
var upd_pswd= req.body.new_password;
log+=upd_pswd+'\n';
var new_data=existing_data;
new_data.password=upd_pswd;
log+=JSON.stringify(new_data)+'\n';
var id= result.docs[0]._id;
log+=id+'\n';
//make PUT request to db
var options={
host:dbCredentials.host,
port:dbCredentials.port,
path:'/'+dbCredentials.dbName+'/'+id,
//url: dbCredentials.url+'/'+dbCredentials.dbName+'/'+id,
method:'PUT',
json:new_data,
headers:{
'Content-Type':'application/json',
'accept':'*/*'
}
};
log+=JSON.stringify(options)+'\n';
var httpreq= http.request(options);
//log+=JSON.stringify(httpreq);
httpreq.on('error', function(e){
response.send('Error'+e.message);
});
response.send(log+'\n\n\nUpdated');
}
});
});
dbCredentials is defined above as follows:
dbCredentials.host = vcapServices.cloudantNoSQLDB[0].credentials.host;
dbCredentials.port = vcapServices.cloudantNoSQLDB[0].credentials.port;
dbCredentials.user = vcapServices.cloudantNoSQLDB[0].credentials.username;
dbCredentials.password = vcapServices.cloudantNoSQLDB[0].credentials.password;
dbCredentials.url = vcapServices.cloudantNoSQLDB[0].credentials.url;
I've tried tinkering around with it, but in the best case scenario, I don't get an error and I see "Updated" but nothing actually happens in the database. Sometimes I get an error saying : 502 Bad Gateway: Registered endpoint failed to handle the request.
If you see what's going wrong, please let me know. Thank you.
This is the documentation on how to update documents in cloudant
UPDATE
Updating a document
PUT /$DATABASE/$DOCUMENT_ID HTTP/1.1 { "_id": "apple", "_rev":
"1-2902191555", "item": "Malus domestica", "prices": {
"Fresh Mart": 1.59,
"Price Max": 5.99,
"Apples Express": 0.79,
"Gentlefop's Shackmart": 0.49 } }
To update (or create) a document, make a PUT request with the updated
JSON content and the latest _rev value (not needed for creating new
documents) to https://$USERNAME.cloudant.com/$DATABASE/$DOCUMENT_ID.
If you fail to provide the latest _rev, Cloudant responds with a 409
error. This error prevents you overwriting data changed by other
processes. If the write quorum cannot be met, a 202 response is
returned.
Example response: { "ok":true, "id":"apple",
"rev":"2-9176459034" }
The response contains the ID and the new revision of the document or
an error message in case the update failed.
I am using bluemix -nodejs and cloudant. The best way that worked for me to do the update is to use the nano package for db interaction from node.js.
You can refer to the post here:
The summary is - By making use of nano api, you can easily update the record. You need to make sure to use - the _id and the right _rev number, while you use nano. This inturn uses PUT Method underneath.
Updating and Deleting documents using NodeJS on Cloudant DB
When you are including nano, make sure to update the package.json to have the nano dependency added. Let me know if you have further questions on the update/delete
When using the cloudant node.js module there is no separate update function.
You need to use the db.insert function also for the update with the right doc revision, so you need to read the latest revision before the insert.
https://github.com/apache/couchdb-nano#document-functions
"and also used to update an existing document, by including the _rev token in the document being saved:"
// read existing document from db
db.get(key, function(error, existing) {
if (!error)
// use revision of existing doc for new doc to update
obj._rev = existing._rev;
// call db insert
db.insert(obj, key, cb);
});
I'm having difficulty in updating an existing metafield with Shopify API. Each time I receive an error, advising me that the variant already exists... so it must be thinking I'm trying to create a new one (and not update).
I thought this may be an issue with 'put' and 'post' - so changed my method to put, however the error persists. I've hardwired in all my variables to make it easier to test.
I'm working with Cloudinary. I'm using https://github.com/sinechris/shopify-node-api with Express.js
app.post('/upload', function(req, res){
// upload page... assume we have uploaded our image - but have hard-wired a local file in for now
cloudinary.uploader.upload('/Users/Rob/Pictures/testimg.jpg', function(savedImg) {
var imageURL = savedImg.url;
console.log(imageURL)
},
{
public_id: "testimg"
});
// the saved image is returned - so we add it to our updateMetafieldData json object
var updateMetafieldData = {
"variant": {
"id": '253818949',
"metafields": [
{
"key": "variant_image_0",
"value": 'testimg', // whatever the public id of our image is.
"value_type": "string",
"namespace": "variant_image"
}
]
}
}
// and post the result to shopify - then redirect to /getvariants
Shopify.put('/admin/variants/253818949.json', updateMetafieldData, function(data){
// res.redirect('/getvariants')
});
});
I actually created Shopify Node API and was just now happened upon this months later but thought I'd answer for anyone else coming along.
Take a look at the shopify API here: https://docs.shopify.com/api/metafield#update
You can update the metafield directly by performing a PUT request against the metafield resource instead of the variant like so:
/admin/metafields/#{id}.json
You would of course need to know the ID of the metafield first so that would require a call to the variant first or you could simply store the id in your local database for reference.