Modify mongodb records before sending to api response - node.js

I'm using MongoDB as a database and express.js to build API.
I want to modify the response before sending it to the client.
here is my express.js code...
products.route("/:id")
.get((req, res) => {
mdb.get().collection('products').find({_id:parseInt(req.params.id), status:1}).toArray()
.then(response => res.status(200).json(response))
.catch(error => console.error(error));
})
Like, I want to add some calculated filed to the response. suppose my response object from the database is this
response = {
id: 1001,
name: 'Apple',
price: 120
}
Now, I want to add image field. so, my final response object will be
response = {
id: 1001,
name: 'Apple',
price: 120,
image: '/assets/images/'+id+'.jpg'
}
Please help me with this I'm very new in express.js

Inside your router handler, you would do something like this:
Products
.findOneAndUpdate({ _id: req.params._id, status: 1 }, {
$set: {
images: '/assets/images/' + req.params._id + '.jpg'
}
},{
new: true
})
.then(product => {
if(!product) return res.status(404).json("Product not found");
else return res.status(200).json(products);
})
.catch(err => {
console.error(err);
return res.status(500).json(err);
});
Use the findOneAndUpdate method from mongodb on your Products collection to update a document inside that collection.
findOneAndUpdate takes three objects as parameters:
first one specifies the query condition
second specifies the updated document
Here we are using the $set operator to add a image property to the existing document.
third is an options object
set new to true to tell mongo to return the updated document instead of the original one, which is the default
Here is a reference to the docs, for more information: https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndUpdate/
EDIT: To modify the response only, you would do inside the .then:
.then(products => {
const modifiedProducts = products.map(product => ({
...product,
image: '/assets/images/' + product._id + '.jpg'
}));
return res.status(200).json(modifiedProducts);
})
Here we are creating a new object for each product that is being returned, by copying its original contents plus the new image property, then returning that modifiedProducts array to the client

Related

NOT WORKING: Mongoose findByIdAndUpdate() does not work

I am trying to create a (MERN stack) management system that keeps track of vacant rooms in a hotel.
I am trying to change the roomTypeAOccupiedTotal from 2 to 3.
From the client side, it sends an axios.put()request as follows:
axios
.put(`http://localhost:8082/api/myHotel/${branchStatus.id}`, data)
this is the server-side code:
router.put('/:_id', (req, res) => {
/*
req.params looks like this:
{ _id: '63b4d533fabbf31cdb519896' }
req.body looks like this:
roomOccupied5F: 3,
roomOccupied6F: 5,
roomTypeAOccupiedTotal: 2,
roomTypeBOccupiedTotal: 8,
*/
let filter = { _id: mongoose.Types.ObjectId(req.params._id) }
let update = { $set: req.body }
MyHotel.findByIdAndUpdate(filter, update, {new: true})
.then(data => res.json({ msg: 'updated successfully' }))
.catch(err =>
res.status(400).json({ error: 'Unable to update the Database' })
);
Below are the GET request and PUT request sent using POSTMAN.
after the message "updated successfully", I sent another GET request to check, but there are no changes to the variable(roomTypeAOccupiedTotal).
Could someone help me with solving this problem? the findByIdAndUpdate() method is working, as its not throwing any errors, but its not updating.
I believe your problem is the filter object. Looking at the docs for findByIdAndUpdate, it expects to receive the id param, not a filter object.
https://mongoosejs.com/docs/api/model.html#model_Model-findByIdAndUpdate
Parameters:
id «Object|Number|String» value of _id to query by
Additionally, when you create an objectId out of the request param, you aren't creating a new instance of it, so whatever was passed in would have failed to match anything. My IDE highlights this for me:
Your fix is likely something like this:
const id = new mongoose.Types.ObjectId(req.params._id)
MyHotel.findByIdAndUpdate(id, update, {new: true})
No need to convert id, and no need to use the update operator(i.e. $set).
try this:
MyHotel.findByIdAndUpdate(req.params._id, req.body, { new: true }, (err, hotel) => {
if (err) {
console.log(err);
} else {
console.log(hotel);
}
});

MERN - update specific string in an array's object

I am using mongoose to connect my backend (Express) server to database. I want to do normal CRUD operations - but I am able to do it only for direct data in object, but I need to be able to access also array data.
Example of my model:
const LeewaySchema = new mongoose.Schema({
name: {
type: String,
},
shirt: [
{
name: String,
image: String,
},
],
With the following code I am able to update only name of the object, but I need to be able to update also name in shirt array
Here is working approach when changing name of object:
app.put('/update', async (req, res) => {
const updateName = req.body.updateName;
const id = req.body.id;
console.log(updateName, id);
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.name = updateName;
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
And I tried the same with shirt array, just specifying the correct path
app.put('/update-shirt', async (req, res) => {
const updateShirtName = req.body.updateShirtName;
const id = req.body.id;
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.shirt.name = updateShirtName; // different path here
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
The server crashes and /update-shirt conflicts with /update path
I am using the same route and frontend for READ
useEffect(() => {
axios
.get('http://localhost:8000/read')
.then((response) => {
setListOfClosets(response.data);
})
.catch(() => {
console.log('error');
});
}, []);
And update name function calling with button onClick:
const updateCloset = (id) => {
const updateName = prompt('Enter new data');
axios
.put('http://localhost:8000/update', {
updateName: updateName,
id: id,
})
.then(() => {
setListOfClosets(
listOfClosets.map((val) => {
return val._id === id
? {
_id: id,
name: updateName,
email: val.email,
}
: val;
})
);
});
};
I don't really know how to do update for shirt's name, I tried to copy paste and just change path and url of course, but it did not work.
The question doesn't actually describe what specific transformation (update) you are attempting to apply to the document. Without knowing what you are attempting to do, there is no way for us to help advise on how to do it.
Say, for example, that the document of interest looks like this:
{
_id: 1,
shirt: [
{ name: "first shirt", image: "path to first shirt" },
{ name: "second shirt", image: "path to second shirt" },
{ name: "third shirt", image: "path to third shirt" }
]
}
Also let's say that the application hits the /update-shirt endpoint with an id of 1 and a updateShirtName of "updated shirt name". Which entry in the array is that string supposed to be applied to? Similarly, how would that information be passed to the server for it to construct the appropriate update.
It is absolutely possible to update documents in an array, here is some documentation about that specifically. But the actual structure of the command depends on the logic that you are attempting to provide from the application itself.
The only other thing that comes to mind here is that the motivation for the schema described in the question seems a little unclear. Why is the shirt field defined as an array here? Perhaps it should instead just be an embedded document. If so then the mechanics of updating the field in the subdocument are more straightforward and none of the aforementioned concerns about updating arrays remain relevant.
just make an update api where you just have to pass the id and and pass the shirt in the findByIdAndUpdate query and hit the postman by passing the below code.
shirt: [
{
name: "jhrh",
image: String,
},
],

how can we use data received through axios put request on client side in mern stack?

I have sent category Id to the Nodejs through this code
const catHandler = async (catId) => {
const data = Axios.put('/api/products/categories/filter', {catId: catId},
{
"headers": { "content-type": "application/json", },
}
).then( categoriesProducts => {
console.log(categoriesProducts.data.products)
})
}
and this is my route for this
router.put('/categories/filter', async (req, res) => {
try {
const findCategory = await Category.find({ _id: req.body.catId });
if (findCategory) {
const productsByCategory = await Product.find(
{ category: req.body.catId }
).then(products => {
res.status(200).json({ products });
})
}
} catch (error) {
console.log('categories filter error', error)
}
})
The products of specific category are being shown in the console.log(categoriesProducts.data.products) on the react front end side like below
0: {_id: "5f7c88756746363148792982", name: "Simple Pizza", price: 5.6, image: "1601996916374.jpg", countInStock: 434, …}
1: {_id: "5f7c88976746363148792983", name: "Smoked Pizza", price: 7.8, image: "1601996951114.jpg", countInStock: 88, …}
2: {_id: "5f7c88c56746363148792984", name: "Large Cheezy Pizza", price: 9.4, image: "1601996997474.jpg", countInStock: 434, …}
But I want to display these products on the front end side. I have tried to use axios.get method but with get method how can I can send category Id to backend. So if any one has any idea how to do that Plz guide me.
you can use the query params with get method in node js
you can get query params by req.query in nodeJs
example
passing category id from front end -
api/products/categories/filter?cid=1
getting query param in the backend
const catId = req.query.cid
You can use below code to pass parameter to API get method.
fetch("/api/products/categories/filter?catId" + catId)
.then((res) => res.json())
.then((json) => {
this.setState({
Items: json,
});
});
And also you first create new state named as Items such as below.
this.state = {
Items: []
};
And finally Iterate on Items.
BR

Node js MongoDb specific page view counter

I'm making app with MEAN stack and I want on every get request to increase viewCounter on specific document ( Property ) inside collection.
If i put this code inside get request of requested property
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
It will increase loading of data and i want to do that after user gets his data.
So is the best way to do this just to send additional request to the database after initial data is loaded ?
Property {
name: '',
description: '',
...,
viewCounter: 5
}
exports.getProperty = catchAsync(async (req, res, next) => {
query = await Property.findById(req.params.id).lean();
if(!query) {
return next(new AppError('No property found with that ID', 404))
}
res.status(200).json({
status: 'success',
data: {
query
}
})
})
Node events can be used to keep the counter of events.
Official document
Reference for code
eventEmitter.on('db_view', ({ parameters }) => {
eventTracker.track(
'db_view',
parameters
);
})
eventEmitter.on('db_view', async ({ user, company }) => {
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
})
Try to send request after making sure your document has loaded.
angular.element($window).bind('load', function() {
//put your code
});

mongoose to display ref object in json

I'm new to mongoose, using the following controller:
const Creport = require('../models/creport.model.js');
exports.save = (req, res) => {
const creport = new Creport({
curso_id: req.body.curso_id,
nombre: req.body.nombre,
....
});
creport.save()
.then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message
});
});
};
in the creport.model.js:
curso_id: {
type: Schema.Types.ObjectId,
ref: 'Curso'
},
this will create a json file like:
{"curso_id":"5b5a14e8ej1a18ac0b5e5433","nombre":"el nombre",....}
while I'm looking for:
{"curso_id":"curso No. 1","nombre":"el nombre",....}
EDIT:
using populate:
exports.findAll = (req, res) => {
Creport.find().populate('curso_id')
.then(creports => {
res.send(creports);
}).catch(err => {
res.status(500).send({
message: err.message
});
});
};
will output:
[{"_id":"5b5ce554967f6a36f0c84fe6","curso_id":{"_id":"5b5a14e8ej1a18ac0b5e5433","name":"curso No. 1"},"nombre":"el nombre"....}]
For return the data in this format, you need to use the aggregate method.
In my tests, I created one course and one Creport and after I executed this aggregate:
CreportModel.aggregate([
{"$match":{_id:creport._id}},
{"$lookup":{
from:"cursos",
localField:"curso_id",
foreignField:"_id",
as:"cursos"
}
},
{"$project":{"curso_id": {"$arrayElemAt":["$cursos.name",0]},"nombre": "$nombre","_id":0}}
])
.then(result=>{
console.log(result)
})
Result:
If you want to add more fields in the result, you need to change the $project phase.
e.g
{"$project":{"curso_id": {"_id":1,"$arrayElemAt":["$cursos.name",0]},"nombre": "$nombre"}}
0 : means that will remove the field in the return
1 : means that will show the field in the return
Mongoose Documentation: Aggregate Lookup
You could use populate method, if you read the mongoose documentation you will find there's very easy way to apply.
http://mongoosejs.com/docs/populate.html
curso_id is an ObjectId. The returned json confirms this.
The semantic is correct.
You should probably add a
curso_number: {
type: Schema.Types.String
},

Resources