How to handle the sequelize unique constraint error? - node.js

I am new to javascript and I need to handle constraint error in sequelize. I searched related to this topic everywhere, but still, I couldn't get a proper workable answer. My attempt it as follows.
app.post('/api/users', (req, res) => {
try {
console.log(req.body);
User.create(req.body)
.then(user=> res.json(user));
} catch (error) {
console.log("Error: "+error);
}});
Here couldn't catch the exception yet. For a valid user input it is able to post the request. So I just need to know a way to handle the exception.

Was looking around for an answer for this, but was not really satisfied with the two given. If you are looking to return a correct response such as a 403 this is the solution I have came up with.
app.post('/api/users', async (req, res) => {
try {
console.log(req.body);
var user = await User.create(req.body)
return res.status(200).json({ status: 'success', result: res.json(user) })
} catch (error) {
if (error.name === 'SequelizeUniqueConstraintError') {
res.status(403)
res.send({ status: 'error', message: "User already exists"});
} else {
res.status(500)
res.send({ status: 'error', message: "Something went wrong"});
}
}
});

You can use Promise with .then().catch(), or use async/await with try/catch
This is Promise
app.post('/api/users', (req, res) => {
console.log(req.body);
User.create(req.body)
.then(user=> res.json(user))
.catch(err => console.log(err))
});
This is async/await
app.post('/api/users', async (req, res) => {
try {
console.log(req.body);
const user = await User.create(req.body);
res.json(user);
} catch (error) {
console.log("Error: "+error);
}
});

Looks like you're mixing two different styles. If you're using then(), then the try catch block is unnecessary:
app.post('/api/users', (req, res) => {
console.log(req.body)
User.create(req.body)
.then(user => res.json(user))
.catch(error => console.log('Error: ' + error))
})
The other style would be using the async package. With async, your code would look like this:
app.post('/api/users', async (req, res) => {
console.log(req.body)
try {
const user = await User.create(req.body)
res.json(user)
}
catch (error) { console.log('Error: ' + error) }
})
Both methods have their advantages and disadvantages that go beyond this snippet and lot of people use both as appropriate, for example the await approach works only inside a function declared with async like in the second example :async (req, res). In such cases, using then() style promise handling is a better approach

Related

Mongo FindOne return results in error parameter

I am doing a login system in NodeJS with a Mongo database, so I try to look in my collection if the user exist. For my tests, I have a user registered, and I'm trying to find him.
My problem is that findOne method return the full user' data as I wish, but in the error parameter, not in the results parameter, and I have no logs to understand why...
Did somebody know why ?
Here is my code:
app.post('/login', (req, res) =>{
console.log(req.body.identifier);
console.log(req.body.password);
client.connect().then(() => {
let newUser = {identifier : req.body.identifier}
res.redirect(req.body.locator)
return client.db(`${process.env.MONGODB}`).collection(`${process.env.MONGOCOLLECTION}`).findOne(newUser).then((err, res) => {
if (err){
console.log("ERROR: "+err.role)
throw err;
}else if(res){
console.log("user found");
console.log(res.role)
}
})
}).catch( e => { console.error(e) }).then(() => {
console.log("--------------------------------");
})
})
And this is what I got :
mail#mail.com
azer
ERROR: USER
{
_id: 6087d850ad9f6f2e0ce97045,
identifier: 'mail#mail.com',
password: '7657d9148a5720dcf4eb4b8bc998498e5d701ce7beb302f398c3d5c0dbd0f857f824b7bfaa45c2a8aba4f85c4ab8b12c99bfb28328e72a89afe11326dc1d3a38349c1c36790a24c910528ada34529e6736ae45f0e5d87ce6b109207e21169bc9b4056fff',
role: 'USER',
sign_up: 2021-04-27T09:24:32.616Z,
name: 'Test',
firstName: 'Test'
}
--------------------------------
This occurs because your code mixes Promises and callbacks. In particular:
findOne(newUser).then((err, res) => {
// (this does not work because .then() takes a function with 1 argument)
})
Promises have a built-in error handling mechanism where a resolver can either resolve or reject the promise, triggering the .then() (success) or the .catch() (failure) branch.
See the note at the top of the relevant manual page. Also, MongoDB provides a short guide on Promises vs. callbacks - pay attention especially to this snippet:
collection
.updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } })
.then(
res => console.log(`Updated ${res.result.n} documents`),
err => console.error(`Something went wrong: ${err}`),
);
Note how .then() accepts two functions above - these are separate callbacks for the "success" case and the "error" case.
Read - Promises and Callbacks
Here you're using promise - then will give the result and catch will give the error.
client.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser)
.then( res => { ... }) // result
.catch(err => { ... }) // error
Callback style
client.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser, (err, res) => { ... })
The mongodb was returning the data, and the data was taken as first argument in the then block named as err, try changing to this:
app.post("/login", (req, res) => {
console.log(req.body.identifier);
console.log(req.body.password);
client
.connect()
.then(() => {
let newUser = { identifier: req.body.identifier };
res.redirect(req.body.locator);
return client
.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser, (err, res) => {
if (err) {
console.log("ERROR: " + err.role);
throw err;
} else if (res) {
console.log("user found");
console.log(res.role);
}
});
})
.catch((e) => {
console.error(e);
})
.then(() => {
console.log("--------------------------------");
});
});

response is undefined using Axios and Express

I have this simple post method in back-end:
router.post('/users', async function(request, response) {
try {
const userToRegister = request.body;
const user = await CreateUserService.execute(userToRegister);
return response.json(user);
} catch (err) {
console.log(err);
return response.status(401).json({ message: 'email already registered' });
}
});
At the front end i'm trying to catch the response if the users is already registered, like this:
api.post('users', user.userFields)
.then(response => {
console.log(response)
})
.catch(err => {
console.log(err);
})
In this case, response is always undefined.
If a pass return response.json(err); in backend it works fine.
What am i missing here?
Nevermind guys, found error.
My fail was in the user catch block of CreateUserService.

Sequelize- Try block won't catch on model.create() error

I want to be able to reroute the website user if an error occurs in adding something to the database (a server-side validation error, like a string containing a non alphanumeric symbol or something). Currently the try-catch block doesn't catch anything even when the model.create() fails. How can I rewrite this to make it get out of the try-block and reroute back to '/' if an invalid entry is attempted?
Here's what I have (btw, Request and Ride1 are the model names):
/* POST request */
router.post('/', function(req, res, next){
/*a bunch of variable initializations to get form data*/
try{
Request.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
.catch(err => {
throw err
})
Ride1.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
.catch(err => {
throw err
})
res.redirect('/thankYou')
}
catch (e){
console.log(e);
res.redirect('/');
}
});
The .catch(err =>...) stuff doesn't seem to do anything and was just a feeble attempt at getting this to work. I think I could do it if I didn't need to Any advice is much appreciated.
Use async/await and the express-async-wrap package:
const wrap = require('express-async-wrap')
...
router.post('/', wrap(async function(req, res, next){
try{
await Request.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
await Ride1.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
res.redirect('/thankYou')
}
catch (e){
console.log(e);
res.redirect('/');
}
}
));

Do I need to return the promise from the entry point of my application?

In express, I have the following code:
app.get('/hello', (req, res) => {
return controller.doSomthingAsync(req).then(() => {
res.send({message: "Hello World"});
})
});
However, I can also do
app.get('/hello', (req, res) => {
controller.doSomthingAsync(req).then(() => { // Return removed
res.send({message: "Hello World"});
})
});
The above also works, but I was wondering if there is any difference between the 2 approaches, and can the second one cause any problems?
Considering the piece of code you provided, there is no difference in using the two versions.
The only benefit of that return is that any piece of code you put after that would not be executed. For instance, suppose the following piece of code:
app.get('/hello', (req, res) => {
if (req.query.foo === 'bar') {
controller.doSomethingAsync(req).then(() => {
res.send({message: "Hello World"});
}).catch(err => {
console.log(err);
res.status(500).send(err);
});
}
controller.doSomethingElseAsync(req).then(() => {
res.send({message: "Hello Universe"});
}).catch(err => {
console.log(err);
res.status(500).send(err);
});
});
This would produce an error since both of the async operations would be performed and try to send the response.
Adding a return in the if block would prevent executing the second async operation and the error.
app.get('/hello', (req, res) => {
if (req.query.foo === 'bar') {
return controller.doSomethingAsync(req).then(() => {
res.send({message: "Hello World"});
}).catch(err => {
console.log(err);
res.status(500).send("Error");
});
}
controller.doSomethingElseAsync(req).then(() => {
res.send({message: "Hello Universe"});
}).catch(err => {
console.log(err);
res.status(500).send("Error");
});
});
EDIT: As Bergi pointed out, using else would do the job as well and avoid to return something that express can't handle.
app.get('/hello', (req, res) => {
if (req.query.foo === 'bar') {
controller.doSomethingAsync(req).then(() => {
res.send({message: "Hello World"});
}).catch(err => {
console.log(err);
res.status(500).send("Error");
});
} else {
controller.doSomethingElseAsync(req).then(() => {
res.send({message: "Hello Universe"});
}).catch(err => {
console.log(err);
res.status(500).send("Error");
});
}
});
No, you don't need to return a promise from your express route, as express does not know what to do with promises (a pity!).
However, every time you call a promise-returning function and do not do further chaining and returning it to your caller, i.e. when you end a promise chain, you are responsible for handling errors from it. So it would be appropriate to do
app.get('/hello', (req, res) => {
controller.getMessageAsync(req).then(msg => {
res.status(200).send(msg);
}, err => {
console.error(err);
res.status(500).send("Sorry.");
});
});

A generic catch-version for exceptions?

I have a sequelize database that validates data and throws errors.
I know I can do something like this to catch and output my errors:
User.build()
.catch(Sequelize.ValidationError, function (err) {
// respond with validation errors
return res.status(422).send(err.errors);
})
.catch(function (err) {
// every other error
return res.status(400).send({
message: err.message
});
But I don't want to add it to every single request, is there some generic way to catch theese errors?
You can add a custom method to req (or res) that will resolve the promise and handle any errors:
app.use((req, res, next) => {
req.resolve = (promise) => {
return promise.catch(Sequelize.ValidationError, err => {
// respond with validation errors
return res.status(422).send(err.errors);
}).catch(err => {
// every other error
return res.status(400).send({ message: err.message });
});
});
next();
});
Usage (provided that the middleware above is added before your routes):
router.post('/user', (req, res) => {
req.resolve(User.build()).then(user => res.json(user));
});
ES.next version (2016):
you can use async functions that throw using this wrapper function copied from the official strongloop website:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
then make the function in your router/controller like that:
router.post('/fn/email', wrap(async function(req, res) { ...throw new Error(); }
and finally have a normal catch all errors middleware:
app.use(function(err, req, res, next) { console.log(err); }
Obviously for this to work you need the babel transpiler currently

Resources