I am creating RESTful API on node.js (express.js + mongoose).
I have mongoose model which has _id and title.
When I handle GET request to find specific object by _id, I use findById method and I want to know how to handle if the requested id was wrong. In other words, the question is "how to handle 404 status for findById method".
I have tried something like this, but it didn't work:
Blog.findById(id)
.select("_id title")
.exec()
.then(result => {
if (result) {
res.status(200).json({
article: result
});
} else {
res.status(404).json({
message: 'Article was not found'
});
}
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
Model.findById in mongoose will return null when no object matches the criteria, so you can simply put an if clause inside .then() before all code
.then(result => {
if(!result) return res.status(404).send('No result found');
// rest of your code here
And that will send a 404 to client.
Related
I build a register page using React as Frontend, and Node Js as backend. However, when I try to check the delicate username. Axios from the Frontend doesn't show any error. I have written the catch in many different ways. But I still cannot find where the problem is. Could anyone help me out? Thanks!
Frontend
const handleSubmit = async (event) => {
event.preventDefault();
if (handleValidation()) {
await axios.post(registerRoute, {
username,
email,
password,
}).then((response) => {
console.log(response);
}).catch((error) => {
if (error.response.status === 11000) {
console.log(error.response.data);
}
})
navigate("/");
}
};
Backend
module.exports.register = async (req, res, next) => {
const { username, email, password } = req.body;
if (!username || typeof username !== 'string') {
return res.json({status: 'error', message: 'Invalid username'})
}
const usernameExit = await Users.findOne({username: username})
if (usernameExit) {
return res.status(11000).json({ message: "Username already exit" });
}
if (!password || typeof password !== 'string') {
return res.json({status: 'error', message: 'Invalid password'})
}
try {
const hashedPassword = await bcrypt.hash(password, 2);
const user = new Users({
username,
email,
password: hashedPassword,
});
user.save();
delete user.password;
return res.json({ status: true, user });
} catch (error) {
if (error.code === 11000) {
return res.status(11000).json({ message: "Username already exit" });
}
}
};
First things first. In your back end endpoint you need to add the word await in before saving the document since you're using async/await for this method.
await user.save();
Second, you won't be able to delete the user's password this way because this is a MongoDB document and not a JavaScript object. You can convert the document to be a JS object which was explained in this question below. After you convert it to a JS object then you do delete user.password, not before.
convert mongodb object to javascript object
Third, you should always follow the industry best practices and use one of the HTTPS codes based on the situation. This way you know that a MongoDB error code is coming from MongoDB and not your endpoint because you setup a different status code like 404 or 400 for any custom error handling that you are doing.
Here's the reference for the HTTPS codes - https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
If you want to catch the status in Axios, you need to set the status like #Yozi mentioned and follow the approach below
return res.status(400).json({message: "Some message here", user});
This way Axios will catch the 400 status and throw an error in the console. If you customize the status like
res.json({status: 'error'});
You need to handle that differently in your front end. You can do something like
console.log(res.data.status === 'error');
return res.json({status: 'error', message: 'Invalid password'})
It returns status 200, which is not an error (axios know nothing about your status: "error" thing.
status 11000 does not sound right to me. Normally it is something below 600. docs. In your case, I would use 422.
So, for all your errors add status(422) and check if it works.
How can I filter the response? I want to show only the name and the phone number. Not email or password. I have also attached a screenshot of the database and the response.
Request Function
router.get('/name/:id', async(req, res) => {
const user = await Model.findOne({ _id: req.params.id });
if (user)
res.status(200).json(user);
else
res.status(500).json({ message: "User doesn't exist" });
})
Here's the DB
Here's the response I am currently getting
2 ways to do it:
First way (filter them manually)
router.get('/name/:id', async(req, res) => {
const user = await Model.findOne({ _id: req.params.id });
if (user)
res.status(200).json({fName:user.fName,sName:user.lName});
else
res.status(500).json({ message: "User doesn't exist" });
})
Second way (use projection from Mongo) Here's an example of me passing the projection object as a second argument to findOne:
router.get('/name/:id', async(req, res) => {
const user = await Model.findOne({ _id: req.params.id },{fName:1,sName:1});
if (user)
res.status(200).json(user); //will give you what you selected in the projection
else
res.status(500).json({ message: "User doesn't exist" });
})
In mongoose you can specify fields to show like below:
// after findOne query and assigning it to user
user.select('name phoneNumber')
Between field names add one space, for more information checkout docs
https://mongoosejs.com/docs/queries.html
I'm implementing user authentication in my Node REST API.
I have defined in the UserController a method to remove a user:
async function remove(req, res) {
try {
User.findOneAndRemove({ _id: req.params.id });
return res.status(200).json({
message: 'user deleted',
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
});
}
}
When I hit the route in Postman with an id from a user stored in the database, I get the success message: "user deleted"
However when I look in the database, the user is still there.
What am I doing wrong?
thanks for your help!
dough, forgot to put 'await' before User.findOneAndRemove ...
I am using mongoose in this example. While trying to delete ,the following error is being shown to me
Cannot DELETE /5e69e2fde0fa464ee01dd68d
I cannot for the life of me figure out what is going wrong .I am a complete beginner in Node.js , MongoDB and creating RESTful APIs
The code given below is the function I am using to delete .
router.delete('/:id', getSubscriber, async (req, res) => {
try {
await res.subscriber.remove()
res.json({ message: 'Deleted Subscriber' })
} catch (err) {
res.status(500).json({ message: err.message })
}
})
and here is the getSubscriber function
async function getSubscriber(req, res, next) {
let subscriber
try {
subscriber = await Subscriber.findById(req.params.id)
if (subscriber == null) {
return res.status(404).json({ message: 'Cannot find subscriber' })
}
} catch (err) {
return res.status(500).json({ message: err.message })
}
res.subscriber = subscriber
next()
}
Any help is appreciated. Thank you for your time.
router.delete('/:id', getSubscriber, async (req, res) => {
try {
//Here try creating an instance of your model.
//I think your schema is named subscriber so
const deleteSuscriber = await Suscriber.remove({_id:req.params.id});
res.json(deleteSuscriber);
} catch (err) {
res.status(500).json({ message: err})
}
});
Here express will put the variable in req.params form the incoming request.
Hope this works!!
Here you can find the documentation on making CRUD Rest API
Making an API with Node/Express + Mongo.
I'm writing some unit test and I observed if I try to get /profile/1 for _id=1 (I let mongo put the ID by default so I cannot have _id=1) i got this error
MongooseError: Cast to ObjectId failed for value "1" at path "_id"
I thought I will have an empty object User.
function getProfile(req, res) {
const userId = req.params.userId
User.findById(userId, "-password", (err, user) => {
if (err) {
console.log(err);
res.status(400)
res.json({
success: false,
err
})
res.end()
return
}
if (!user) {
res.status(404)
res.json({
success: false,
message: `Cannot find an User with the userId: ${userId}`
})
res.end()
return
}
res.json({
success: true,
user: user
})
res.end()
return
})
}
My test :
describe('Test /profile route', () => {
it('shouldn\'t find Joe Doe\'s profile with a wrong ID\n', (done) => {
chai.request(server)
.get(`/profile/1`)
.end((err, res) => {
expect(res).to.have.status(404)
done()
})
})
I thought I would have an error 404 (second if, and I know it's not the right code error, just a quick way for me to see where my test goes) but I got a 400 -> meaning an error is return.
I read mongoose documentation and I don't really see ho they explain the return value with the different methods.
The problem is that '1' is not a valid mongoose object id. Therefore it is trying to compare different types.
Try casting it to a object id like so:
userId = mongoose.Types.ObjectId(userId)
and then run your query
User.findById(userId, "-password", (err, user) => { .... });