How to Throw custom Errors? - javascript

I am trying to use passport-jwt strategy for authentication.
Here is my code :-
router.post('/register', async (req, res) => {
const { username, email } = req.body;
try {
const user = await User.findOne({ username });
if (user) {
throw new Error('User with same username already exists !!');
}
const newUser = new User({
username,
email
})
const salt = await bcrypt.genSalt(10);
newUser.password = await bcrypt.hash(req.body.password, salt);
const savedUser = await newUser.save();
res.json({
status: 200,
'Content-Type': 'Application/Json',
'message': `You have successfully regirstered yourself, ${savedUser.username}!`
})
} catch (err) {
err.statusCode = 500;
console.log(err.message);
res.header({
status: '200',
'Content-Type': 'Application/Json',
});
res.json(err);
}
});
Now this route is working just fine, it's doing all the things till now. The only problem is, When i find an existing user, I want to throw a new error with a custom message. Javascript has this Error class which I can use to throw these errors.
The problem occurs when it catches error. When I console.log(err.message), I can see my custom message perfectly. But the err object that I am returning in response via res.json(err) does not have any message but only statusCode.
I want to know why this is happening and what's the solution for this ? Right now, I am doing this by doing something like this :-
res.json({
statusCode: 500,
message : err.message
});
But I would like to return the err object with both statusCode and message fields populated.

You can create your own Error class which can take more than one parameter in the constructor. This class has to extend base Error JavaScript class. For example:
class MyCustomError extends Error {
constructor(msg, statusCode) {
super(msg);
this.statusCode = statusCode;
this.name = MyCustomError.name;
}
}
function throwCustomError() {
throw new MyCustomError('Some custom message', 404);
}
try {
throwCustomError();
} catch (error) {
console.log(error.message);
console.log(error.statusCode);
console.dir(error);
}
Remember that you have to call super on the beginning of the constructor if you are extending another class

You are passing the error object to the json method of the response object. But this only takes JSON parseable string as a parameter. What you can do is use -
res.json(JSON.stringify(err))
and at the place where you are using this response, you need to parse this string as JSON and then convert it into an Error object. You can use the following syntax assuming your front-end also uses javascript
err = new Error(JSON.parse(response.data))

Replace the entire catch block with the following line of code.
res.status(500).json({
message: err.message
})

From the documentation of res.json() : This method sends a response (with the correct content-type) that is the parameter converted to a JSON string using JSON.stringify().
Now running JSON.stringify(new Error('custom msg')) we get "{}"

Related

document not getting deleted in mongo using jwt token

Task of code : Is to delete document from DB using JWT token
error : sending 500 internal error
code of Auth.js
const auth=async(req,res,next)=>{
try{
const token=req.header('Authorization').replace('Bearer ','');
const decoded=jwt.verify(token,'helloworld');
const user=await User.findOne({_id:decoded._id,'tokens.token':token});
if(!user){
throw new Error();
}
// console.log(token);
req.token=token;
req.user=user;
next();
}
catch(err){
// console.log(token);
res.status(401).send({error:"please authenticate"});
}
}
module.exports=auth;
Code To delete document (API endpoint)
router.delete('/users/me',auth,async(req,res)=>{
try{
await req.user.remove();
res.status(201).send(req.user);
}
catch(err){
console.log(err);
console.log(req.user);
res.status(500).send();
}
})
The Problem is even if I am sending incorrect JWT token it should give me {error : please authenticate} but I am not getting this error too instead I am getting 500 internal error and same error when sending correct JWT .
Even I am printing error in console ,its not showing error in console
In your API endpoint, you have to call your collection name then you need to remove with query/params id.
If you are using mongoose Like this and send id from frontend as query/params,
Model.remove({ _id: req.body.id }, function(err) {
if (!err) {
message.type = 'notification!';
} else {
message.type = 'error';
}
});
or
router.delete('/users/me/:id', auth, async(req,res) => {
try {
await Model.deleteOne({ _id: req.params.id})
res.status(201).send(req.user);
} catch(err) {
console.log(err);
res.status(500).send();
}
});
I hope if you follow this way you can solve this problem.
This is the wrong way to remove await req.user.remove(); Because, when code executes it will don't know which collection of data needs to remove.

I am getting TypeError is not a function in nodeJS

I have a login route but whenever it's giving me a typeError not a function. I have checked the code too many times but still can't get why it's giving me this error:
Here's the code:
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).send("Please provide an email and password");
}
const user = await User.find({ email });
if (!user) return res.status(401).send("User not found");
const isMatch = await user.checkHashedPassword(password);
if (!isMatch) return res.status(401).send("Invalid credentials");
sendTokenResponse(user, 200, res);
} catch (ex) {
console.log(ex);
}
});
The error I get is that user.checkHashedPassword is not a function.
Here's the checkHashedPassword method in userSchema:
userSchema.methods.checkHashedPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
Here's the complete error that I get:
TypeError: user.checkHashedPassword is not a function
at D:\pythonprogs\todoapp\routes\users.js:46:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
I have checked the spellings and everything even changed the function name to see if it works but don't know why it's giving this error. Please help
problem is you are using find() method instead of findOne().
find() returns array of collections not object. try this:
const isMatch = await user[0].checkHashedPassword(password)

Is my code the best way to use async await?

Am try to implement and learn async await functions in my login example, but I don't know if is the best, elegant and clean code. I have doubs meanly in catch errors, and if I need implement in a best way the const and functional programing. Can share your opinions?
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw res.status(404).json({error: 'El usuario no existe'});
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw res.status(404).json({error: 'No match'});
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // send error
res.status(404).json(e);
}
}
THANKS
Your code shows a couple problems:
You're attempting to send double responses. First you do throw res.status(404).json(...). Then, you catch that exception and do res.status(404).json(e) again. That's not right. If you're going to send the response, then just return, don't throw. Or, just throw the exception without sending a response and send the actual error response from the catch handler.
Also, throw res.status(404).json({error: 'No match'}); sends the response and then throws whatever .json() returns which is probably not what you want. That won't be an error object of any kind.
I prefer to centralize the places I send an error response to one place in the request handler. That keeps you from ever attempting to send multiple responses and just makes the flow of the request handler easier to understand (in my opinion).
To do that, I just throw a custom error that may have a custom message/status associated with it and then catch all possible errors in one place. Here's one way to do that. The myError class can be used everywhere in your project, not specific to just one route. The idea is that often when you throw, you know in that context what you want the status and message to be so you set that in the custom Error object and can then use that info in the catch. The catch then has to determine whether it has your custom error or just a regular error. First, I have a reusable Error subclass that lets me throw, not only a message, but also a status value.
// reusable error class that contains a status in addition to the message
class MyError extends Error {
// this static method saves having to compare if it's a custom error object or not
// every time we use this
static sendError(res, e, status = 500) {
if (e instanceof MyError) {
e.sendError(res);
} else {
res.sendStatus(status);
}
}
constructor(msg, status = 500) {
// allow calling with or without new
if (!(this instanceof MyError)) {
return new MyError(msg, status);
}
super(msg);
this.status = status;
}
sendError(res) {
res.status(this.status).send(this.message);
}
}
And, then here's how you use that in your code and centralize the sending of the error status.
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw MyError('El usuario no existe', 404);
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw MyError('No Match', 404);
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // log and send error response
// e may be either MyError or some other system generated Error
console.log(e);
MyError.sendError(res, e);
}
}

Can't use #Res() with FilesInterceptor()

I am trying to upload a file using builtin multer and after then sending the response back to the user for success or failure. It was all going good until today, when I try to upload the Response wont come. after digging a bit I find out that when i use #res with #UploadedFile it does not execute the controller. I am new to nest.js.
Working.
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar) {
console.log(req.body);
if (!req.body.user_id) {
throw new Error('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
); // returns the url for the uploaded image
return resultUpload;
} catch (error) {
console.log(error);
return error;
}
}
Not Working.
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar, #Res() res) {
console.log(req.body);
if (!req.body.user_id) {
throw new Error('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
); // returns the url for the uploaded image
return resultUpload;
res.send(resultUpload);
} catch (error) {
console.log(error);
res.send(error);
}
}
In nest, you should always avoid injecting #Res because then you lose a lot of things that make nest so great: interceptors, exception filters,...
And actually, in most cases you don't need #Res since nest will automatically handle sending the response correctly.
If you want to send data from a controller method, you can just return the data (Promises and Observables will be resolved automatically as well). If you want to send an error to the client, you can just throw the corresponding HttpException, e.g. 404 -> NotFoundException:
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar) {
if (!req.body.user_id) {
// throw a 400
throw new BadRequestException('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
);
return resultUpload;
} catch (error) {
if (error.code === 'image_already_exists') {
// throw a 409
throw new ConflictException('image has already been uploaded');
} else {
// throw a 500
throw new InternalServerException();
}
}
}
If for some reason you have to inject #Res here, you cannot use the FilesInterceptor. Then you have to configure the multer middleware yourself.
Side note
You can create a custom decorator for accessing the userId:
import { createParamDecorator } from '#nestjs/common';
export const UserId = createParamDecorator((data, req) => {
if (!req.body || !req.body.user_id) {
throw new BadRequestException('No user id given.')
}
return req.body.user_id;
});
and then use it in your controller method like this:
#Post('uploads/avatar')
async uploadFile(#UserId() userId, #UploadedFile() avatar) {
look, when you are using an interceptor, you are handling (with using .handle()) the stream of response (observable) not a whole package of it, but using express #Res actually is somehow getting around the whole flow of response streaming.
this is also explicitly mentioned in nestjs official documents:
We already know that handle() returns an Observable. The stream
contains the value returned from the route handler, and thus we can
easily mutate it using RxJS's map() operator.
WARNING
The response mapping feature doesn't work with the
library-specific response strategy (using the #Res() object directly
is forbidden).

How to throw an error inside the pre handler in Hapi.js

I started using v17 of Hapi.js and I am running into issues when using the pre-handler.
I want to save a user into a database, but first I use the pre-handler to check if a user already exists. If the user exists, I want to throw an error. The structure of my route is as so...
module.exports = {
method: "POST",
path: "/users",
config: {
auth: false,
pre: [{ method: verify_unique_user}],
handler: create_user.create
}
}
The content of verify_unique_user is...
async function verify_unique_user(req, h) {
await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
},
(err, user) => {
if (user) {
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
}
});
return req;
}
Let's assume the user already exists in the database. Then an error will be thrown from either of the if statements. When this happens, I get the following error...
events.js:167
throw er; // Unhandled 'error' event
^
Error: Username taken!
at User.findOne (/Users/ericbolboa/Desktop/Warble/server/src/users/util/user_function.js:16:16)
This crashed my server. This is not what I want. If I throw an error in my handler function, the response looks like this...
{
"statusCode": 400,
"error": "Bad Request",
"message": "error"
}
But whenever I throw an error in the pre-handler, my server crashes. How can I throw errors properly?
Not sure if this is the source of the issue but you can simplify the async/await instead of using the callback
async function verify_unique_user(req, h) {
const user = await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
});
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
return req;
}
Take a look at the toolkit(h) and options.response.failAction of route.
A route can set response.failAction in options. There, you can format error messages, and send response, however you please. That includes errors thrown from pre handlers.
Edit: Every pre-handler can have it's own 'failAction' handler. You must do a response(...).takeover() if you want to halt the chain.

Categories

Resources