I'm new with node.js. I'm writing a single rest API and I want to know what is the right way to expose data without expose unnecessary information.
In my example, I have a "Tag" schema. And I don't want to expose the mongoose fields to my client. My code:
apiRoutes.get('/tag', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Tag.find({}, (err, tags) => {
return res.json(tags);
});
});
But in client, I don't want to expose "_id" and "__v":
{
"_id": "57083a5e725f3cf0242a2916",
"tagName": "Test",
"en_us": "Testing",
"__v": 0,
"lastUpdated": "2016-04-08T23:10:22.759Z"
}
What is the right way to map only relevant fields?
Your can always use mongoose virtuals. Here is an example:
In your model, you can use something like this:
Tag
.virtual('public')
.get(function() {
return {
tagName: this.tagName,
en_us: this.en_us,
lastUpdated: this.lastUpdated
};
});
Then, when making a query, just use the virtual you've created:
Tag.find({}, (err, tags) => {
res.json(tags.map(tag => tag.public));
});
You can use Schema transform toJSON. Here is a blog post explains it in details ignore certain fields from mongoose schema when return object to client
TagSchema.set('toJSON', {
transform: function(doc, ret, options) {
delete ret._id;
delete ret.__v;
return ret;
}
});
You can declare required fields separated by space in find method:
Tag.find({}, 'tagName en-us', (err, tags) => {
return res.json(tags);
});
Check mongoose documenation.
Related
I am trying to develop a data aggregation feature for an application. The user should be able to enter a name and it'll search the database collection for that name and return the collection.
I am trying to implement it using node.js but i am confused as to how to pass a parameter for the name.
Controller:
exports.DAFacility_author_search = (req, res) => {
DAFacility.aggregate([
[
{
'$match': {
'author': author
}
}
]
]).then((DAFacility) => {
res.send(DAFacility);
})
.catch((e) => {
res.send(e);
});
};
Route:
router.get('/DAFacilityAuthor', DAFacilityController.DAFacility_author_search)
For the 'author': author i am trying to use author as the variable name but i dont exactly know how to structure the controller and router to take in a parameter so that i can retrieve a value on postman.
Any help would be great
Thank you
I suppose that your request in postman will be something like:
{
"author": "foo",
...
}
When your request is received by the API, the body is available in your req object in the controller, so, if you want to use author field in your aggregate, you simply access: req.body.author.
Your controller could have the next structure:
export const DAFacilityController = {
DAFacility_author_search: (req, res) => {
DAFacility.aggregate([
{'$match': {'author': req.body.author}}
])
.then((DAFacility) => {
res.send(DAFacility);
}).catch((e) => {
res.send(e);
});
},
DAFacility_another_method: (req, res) => {...},
}
The router is ok:
router.get('/DAFacilityAuthor', DAFacilityController.DAFacility_author_search);
router.get('/DAFacilityAuthor/<something>', DAFacilityController.DAFacility_another_method);
I hope I helped
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,
},
],
I need to find specific data in my MongoDB for the rest API but it's returning null.
app.get('/api/v1/champions/:name', (req, res) => {
db.find({"champions": req.params.name}, (err, champion) => {
res.json(err)
})
})
Here is my MongoDB Schema:
champions: {
champ_name_1: {
dmg: Number,
cost: Number
},
champ_name_2: {
....
}
}
Since you are checking to see if a key exists in the champions object you'll need to write the query differently.
If your data was formatted like this then your query would work. (champions is a String)
{
"champions": "champ_name_1",
"dmg": 123,
"cost": 123
}
To check if a key exists in an object in mongo, use a query like this.
const champKey = 'champions.' + req.params.name;
db.find({ [champKey]: { $exists: true } });
https://docs.mongodb.com/manual/reference/operator/query/exists/
you can use mongoose package
here
and simply use the mongoose's find() methond to find particular data as providing to it.
For Example: find({_id: MY_ID_HERE});
I have a schema with sub objects, i want to be able to update a specific key inside of it. If i update only a specific key - like in the Post example - it's empty all the other keys..
for example :
{
"_id": "32323323",
"names":{
"firstname":"John",
"lastname":"foo",
"workers":{
"position":"manager",
"address":"1 st"
}
}
}
I want to update Only "position" key via Post request , for example :
$.post({
url: 'workers/information/',
data: {
user_id: user_id,
names: {
workers: {
position: some data,
}
}
},
success: function (result) {
alert('Your information updated successfully')
}
});
Here is the update method in NodeJs server :
UserDataController.updateWorkersInformation = function (userID, workersInformation, cb) {
if (userID) {
user.findOneAndUpdate({_id: userID}, workersInformation, function (err, result) {
if (err) return cb(err);
return cb(null, result);
});
}
};
You may want to look into mongoose. It provides a more simple interface than the native client does.
https://www.npmjs.com/package/mongoose
However, as the comment mentioned, you are missing the $set operator. {$set:workersInformation}
If update is called without the $set operator, the entire document will be replaced with your update object.
http://mongodb.github.io/node-mongodb-native/2.2/tutorials/crud/
I have a collection of fixtures that 'belong' to a competitor and look something like this:
{
"_id": {
"$oid": "59dbdf6dbe628df3a80419bc"
},
"timeOfEntrance": "1507581805813",
"timeOfFinish": null,
"competitor": {
"$oid": "59db5a3f3d6119e69911a61a"
},
"__v": 0
}
My goal is to update only the document's timeOfFinish by sending a PUT request with competitor's ID as a param in the url and timestamp in the body. However I'm struggling to compose the update query.
The following is what I have currently, it never finds the right match and to my surprise it's always updating the wrong document.
fixtureController.put = (req, res) => {
const competitorId = req.params.id;
const timeOfFinish = req.body.timeOfFinish;
Fixture.findOne({'competitor.$oid': competitorId}, (err, fixture) => {
fixture.set({ timeOfFinish });
fixture.save()
.then(updatedFixture => {
return res.status(200).json({
success: true,
fixture: updatedFixture
})
})
.catch(err => {
return res.status(500).json({
message: err
});
});
});
};
Bit of a beginner in the MongoDB field, will appreciate your comments and solutions.
Turns out there was no need to specify the exact field in the match parameter. Mongoose matches by the id field automatically.
Fixture.findOne({'competitor': competitorId}, (err, fixture) => {
...
});