How to show Created By "NAME" using MongoDB - node.js

I am new to MongoDB, so I was wondering if there any way to show the name of created by or modified by XYZ user? I want it in my application to display the name who created or modified something.

You need to account for this info when structuring your data model. E.g. let's say you're writing posts to the posts collection, and want to add the post author from authors collection.
Now, the simplest way to do this is to have this data directly embedded in your post document. E.g. for creation data we use insert, something like this:
// posts.service.js
function save(postData, userData) {
// We return a Promise here
return db.posts.insert({
title: postData.title,
text: postData.text,
published: true,
// now comes your audit data
createdBy: user.username,
createdAt: new Date().toISOString(),
});
}
module.exports = { save };
You use it like this, ie. in your /posts API controller:
// ... other stuff then:
const postsService = require('./posts.service');
route.post('/posts', function(req, res, next) {
postsService.save({
title: req.body.title,
text: req.body.text,
}, req.user)
// Handle response and error.
.then(response => res.json(response))
.catch(error => next(error));
And for updating a post, you'd add this to posts.service.js (using update):
// posts.service
// ...after your *save* function above
function update(postId, postData, userData) {
return db.posts.update({
id: postId,
}{
title: postData.title,
text: postData.text,
published: true,
// now comes your audit data
modifiedBy: user.username,
modifiedAt: new Date().toISOString(),
});
}
// update module exports:
module.exports = { save, update };
Now, on your controller, add the route to handle updates:
// ... after the creation route
route.put('/posts/:postId', function(req, res, next) {
postsService.update(req.params.postId, {
title: req.body.title,
text: req.body.text,
}, req.user)
// Handle response and error.
.then(response => res.json(response))
.catch(error => next(error));
Now, other ways to do this can mean you only include reference (ObjectId) of whomever modified the data. Or maybe more info.
But smarter and easier thing to do would be to use something like mongoose for handling your data, and then use a plugin that does all this automatically. Something like this: https://github.com/taptaptech/mongoose-audit.
You could look for something similar at npm: https://www.npmjs.com/search?q=mongoose%20audit.
What these things do is they add pre-save hooks to your documents and in those hooks the audit data is tracked. So you can use something finished, or you can look at what these packages do and try to replicate the functionality - better if this is a hobby project and you wanna learn how things work.
Now, how do you show this information in your frontend is likely good for a new question, as you specify no information about any of the software/packages you're using.

you should just write it down in your db upon edit/create, there is no build in functionality for this.

Related

Deleting object from mongoose database not working if site is not refreshed

app.delete("/api/persons/:id", (req, res, next) => {
Person.findByIdAndDelete(req.params.id)
.then((result) => {
res.status(204).end();
})
.catch((error) => next(error));
});
Not sure how to even explain this properly, but there is my delete method. It works fine for objects that are allready in the databases, but if I add a new one and I dont refresh the site, I get error: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Person"
Below is my mongoose schema if that helps:
const personSchema = new mongoose.Schema({
name: { type: String, required: true },
number: { type: Number, required: true },
});
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
My guess is you're optimistically updating your frontend with the new Person without waiting for a successful DB response with the new data. That is a valid technique, but gets you into trouble if you're not careful.
My suggestion would be to also send the new value from the database back to your app right away say it can stay in sync. You likely have no _id value on the front end if you're optimistically updating the app before a DB response.
Something like this:
app.post("api/person/new", async (req, res) => {
try {
const person = new Person(req.body)
await person.save()
res.status(201).send({ person })
} catch (err) {
res.status(500).send({ err })
}
})
And then more importantly, your API handler on the frontend needs to take that returned person value and use it to update the values on your front end, so it has access to the _id property for immediate deletion. And if there's an error creating the person for any reason, you can remove the person from the front end, or handle it however you wish.
I don't know what your app is built with, so I can write a sample bit of code for it.

how to stop users from viewing and updating another user's data in node.js?

I am storing a parking detail with a merchant id in the mongoose schema since a parking belongs to a certain merchant user and it cannot be empty or null.
Here is the model:
const parkingSchema = new mongoose.Schema({
merchantId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Merchant",
},
//other details
})
merchant model is something like this:
const merchantSchema = new mongoose.Schema({
merchantId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Auth",
},
//other details
})
And finally the auth schema:
const authSchema = new mongoose.Schema({
accountType: {
type: String,
required: true,
trim: true,
default: "user",
enum: ["merchant", "user", "provider"],
},
//other details
})
If the original user wishes it, I simply want to update the parking data; otherwise, I want to throw an error.
I am using jsonwebtoken to authenticate users.
Here is the query to update the data:
exports.updateParking = async (req, res) => {
try {
const { parkingName, price, address, name, phoneNumber, about } = req.body;
const { parkingImage } = req.files;
const check_exist = await Auth.findById(req.data.id);
if (!check_exist) return res.status(404).json({ error: "User not found" });
console.log(req.data.id);
const updateData = await Parking.findByIdAndUpdate(
{ _id: req.params.id, merchantId: req.data.id }, // I think here is the problem
{
$set: {
parkingName,
price,
address,
...
},
}
);
return res.status(200).json({
success: true,
msg: "Parking has updated successfully",
});
} catch (error) {
return error.message;
}
};
However, the issue is that other users can now update another user's data which I want to stop
below is the query of middleware:
routing.patch("/parking/update/:id", middleware.authenticateToken, merchant.updateParking)
You should be showing each user only their parkings that they have created or belong to them.
const myParkings = async (req, res) => {
// always await code in try/catch block
const merchants = await Parkings.find({ user: req.user._id })
.. then populate the fields that you want to show
res.status(200).json({
success: true,
bookings,
});
};
you have to set this req.user._id when user logins. You could create a session.
I think what you're looking for is something like CASL Mongoose (or a similar package), and more specifically, the "conditions" section of the CASL docs.
What you're dealing with here is the distinction between 2 concepts:
AuthN (authentication) - determines who someone is and whether they are "authenticated" to make an API request
AuthZ (authorization) - determines what the authenticated user is allowed to do
In your app, middleware.authenticateToken is responsible for the AuthN piece of the equation. It makes sure that only users that have created an account are able to make requests to your API routes.
What you still need to solve for is the AuthZ piece, which can be done in a bunch of different ways, but one popular one is to use CASL, which is a Node AuthZ library that allows you to utilize your ORM's native query syntax to limit actions based on the authenticated (AuthN) user's attributes.
In other words, you can do something like, "Only allow user with ID 1 to update Parking entities that he/she owns". Below is generally what you're looking for (not tested for your use case, but the general idea is here):
const casl = require('#casl/ability');
// Define what a `Auth` (user) can do based on their database ID
function defineMerchantAbilities(merchantUser) {
const abilities = casl.defineAbility((allow, deny) => {
// Allow merchant to update a parking record that they own
allow('update', 'Parking', { merchantId: merchantUser.id })
})
return abilities
}
exports.updateParking = async (req, res) => {
const userId = req.data.id
const parkingId = req.params.id
// Find your merchant user in DB (see my comments at end of post)
const merchantUser = await Auth.findById(userId)
// Find your parking record
const parking = await Parking.findById(parkingId)
// Pass user to your ability function
const ability = defineMerchantAbilities(merchantUser)
// This will throw an error if a user who does not own this Parking record
// tries to update it
casl.ForbiddenError
.from(ability)
.throwUnlessCan('update', casl.subject('Parking', parking))
// If you make it here, you know this user is authorized to make the change
Parking.findByIdAndUpdate( ...your code here )
}
Additional comments/notes:
I would recommend removing your try/catch handler and using an Express default error handler as it will reduce the boilerplate you have to write for each route.
I would also recommend writing a middleware that finds a user by ID in the database and attaches it to a custom property called req.user so you always have req.user available to you in your authenticated routes.

How to update document in MongoDB through Mongoose

I am trying to update my document field with the following code, but I catch an error "Cannot GET /updation".
My code:
router.put('/updation', (req, res) => {
const query = { email: "babra#arzepak.com" };
const newEmail = { $set: { name: "babra", email: "nadralatif952#gmail.com" } };
Registration.updateOne()
.then((registration) => {
console.log("updating");
res.render('index', { title: 'updating registrations', registrations });
})
.catch(() => {
res.send('Sorry! Something went wrong.');
});
});
send a request with PUT method and you didn't use of query and newEmail as arguments for updateOne() pass them, and in new version of mongoose you don't need to $set for updating so change your code like this :
router.put('/updation', (req, res) => {
const query = { email: "babra#arzepak.com" }
const newEmail = { name: "babra", email: "nadralatif952#gmail.com" }
Registration.updateOne(query,newEmail)
.then((registration) => {
console.log("updating")
res.render('index', { title: 'updating registrations', registrations });
})
.catch(() => { res.send('Sorry! Something went wrong.'); });
})
Wrong Method
In your router you registered a route '/updation' for PUT method.
Hence only request with method PUT will be handled by your router. The error Cannot GET /updation means you are trying to handle a request with GET method which is simply not registered by your router.
A - If this code is supposed to get triggered when you visit the page via browser, then you should register the route for GET method, like router.get('/updation', ... ),
or
B - If this code is supposed to run seperate from your view (like a REST API should), then use a PUT request and end the request inside your router with res.status(200).json(yourdata)
📑 Sidenote :
It looks like you did not completely setup your mongoose query, I mean you defined the query but you don't use it in the database request. To use the defined query you propbably should change your code, according to the mongoose docs : https://mongoosejs.com/docs/api/model.html#model_Model.updateOne . I am not including a specific code example here, since it's not part of the question.

How to create a delete method in nodejs(express) with sequelize(sqlite) as database

I have been trying yesterday and still continuing today to figure out how to create a nodejs delete method so I can delete data from database based on ID.
I have tried different code from google/youtube/stackoverflow etc but nothing has worked so far.
The error I have with this code is that data.query is not a function. Data is a variable on my code.
If anyone has any idea how to fix please help.
app.js
app.delete('/zoom/:id', function(req, res) {
data.query('delete from', [req.params.id]);
res.render('deleted')
});
data.js
var Data = sequelize.define('data', {
subject: Sequelize.STRING,
MEETINGID: Sequelize.STRING,
Password: Sequelize.STRING
});
Sequelize destroy method seems like a suitable one. E.g.
app.delete("/zoom/:id", function (req, res) {
data.destroy({
where: {
// criteria
},
});
res.render("deleted");
});
To set a criteria which i suitable for your situation, you will need to take a look at sequelize syntax. I found some examples and may be you can modify them to your needs. Depending on your database structure.
where: {
'$car.id$': 2
},
where: {
furniture_type: 'leather'
},
where: {
id: {
$notLike: { $any: someValue }
}
},

Looking for Best Approach For update one in Mongoose

I am still kind of new to Mongoose in general. I am building my blogging app which has backend based on Node and MongoDB, I am using Angular for frontend.
I am creating my Restful API which is supposed to allow user click on a post and update it. However, I don't know for sure whether I am doing it the right way here.
This is the schema for my post:
const mongoose = require('mongoose');
// Schema Is ONly bluePrint
var postSchema = mongoose.Schema({
title: {type: String, required: true },
content: {type: String, required: true},
}, {timestamps: true});
module.exports = mongoose.model("Post", postSchema);
In my angular service, I have this function to help me to send the http request to my backend server, the id for this function comes from backend mongoDB, title and content is from the form on the page
updatePost(id: string, title: string, content: string) {
console.log('start posts.service->updatePost()');
const post: Post = {
id: id,
title: title,
content: content
};
this._http.put(`http://localhost:3000/api/posts/${id}`, post)
.subscribe(res => console.log(res));
}
It appears to me that there are at least couple of ways of approaching this for creating my API
Method 1 ( works but highly doubt if this is good practice):
here I am passing the id retrieved from mongoDB back to server via my service.ts file to avoid the 'modifying immutable field _id' error
app.put("/api/posts/:id", (req,res)=>{
console.log('update api called:', req.params.id);
const post = new Post({
id: req.body.id,
title: req.body.title,
content: req.body.content
});
Post.updateOne({_id: req.params.id}, post).then( result=> {
console.log(result);
res.json({message:"Update successful!"});
});
});
Method 2 I consider this is more robust than method 1 but still I don't think its good practice:
app.put("/api/posts/:id", (req, res)=> {
Post.findOne(
{_id:req.params.id},(err,post)=>{
if(err){
console.log('Post Not found!');
res.json({message:"Error",error:err});
}else{
console.log('Found post:',post);
post.title=req.body.title;
post.content=req.body.content;
post.save((err,p)=>{
if(err){
console.log('Save from update failed!');
res.json({message:"Error",error:err});
}else{
res.json({message:"update success",data:p});
}
})
}
}
);
});
I am open to all opinions in the hope that I can learn something from guru of Mongoose and Restful : )
Justification to Choose findOneAndUpdate() in this scenario in simple words are as follow:
You can use findOneAndUpdate() as it updates document based on the
filter and sort criteria.
While working with mongoose mostly we prefer to use this function as compare to update() as it has a an option {new:
true} and with the help of that we can get updated data.
As your purpose here is to updating a single document so you can use findOneAndUpdate(). On the other hand update() should be
used in case of bulk modification.
As update() Always returns on of document modified it won't return updated documents and while working with such a scenario like
your we always returns updated document data in response so we
should use findOneAndUpdate() here

Resources