How do you detect UniqueConstraintError in Sequelize? - node.js

I want to retry creating record in case unique attribute is already there, so how do I check if failure to create new record is due to UniqueConstraintError.
I'm trying to find an example code but haven't been able to find anything.

import {UniqueConstraintError} from 'sequelize'
Model
.create({...})
.then(obj => {})
.catch(err => {
if(err instanceof UniqueConstraintError){
throw new Error('duplicate error')
}
else{
throw err
}
})
If you also need the field name (which caused this error) this might help
err.errors[0].path

Model.create(objectToCreate)
.catch(sequelize.UniqueConstraintError, () => { // TODO });
If you use bulkCreate you can use boolean property in option ignoreDuplicates (not supported by Postgres) or updateOnDuplicate (only supported by MySQL)

Related

in Typescript, try...catch error object shows "Object is of type 'unknown'.ts(2571)"

router.get('/cells', async (req, res) => {
try {
const result = await fs.readFile(fullPath, { encoding: 'utf-8' });
res.send(JSON.parse(result));
} catch (err) {
if (err.code === 'ENOENT') { // Object is of type 'unknown'.ts(2571) (local var) err: unknown
await fs.writeFile(fullPath, '[]', 'utf-8');
res.send([]);
} else {
throw err;
}
}
err.code make this ts error : Object is of type 'unknown'.ts(2571)
I definitely know that err.code exists, so I want to know how to define the type(or interface) of err?
(tsc version info : My global typescript version is v4.3.5 and the above code belongs to my project which has typescript v4.1.2)
--- Edit ---
I finally know why this error happens to me.
I thought I used tsc version under 4.3.x, but it turns out I used v4.4.x.
In vscode, cmd + shift + P and search for Select Typescript version
and I actually used v4.4.3, (I mistakenly thought on version because I only check tsc version from terminal)
Thanks for sharing Youtube video,
Just very recently, Typescript has been update to make error object inside catch be unknown instead of any
which means, your code looks something like this to your compiler
catch(e: unknown) {
// your logic
}
Provide your own interface and save into another variable to avoid this error:
catch(e : unknown) {
const u = e as YourType
// your logic
}
You can still use any there, but it's not recommended.
As stated in other answers, since TypeScript 4.4, errors are automatically cast as unknown, so you can't do anything with them without type checking. Unfortunately ErrnoExceptions are not implemented as an importable class, but rather just a regular Error with additional properties plugged in. It is a type interface in #types/node, but you can't use isinstance to check against it since there's no class definition for this exact error, so checking isinstance against Error will not let you access the err.code property. That being said, you can make the compiler happy with:
OK method
try {
await fs.readFile(file);
catch (err: NodeJS.ErrnoException) {
if (err?.code === 'ENOENT') return;
else throw err;
}
The caveat here is if you simply do if (err.code)... and forget the ?, the compiler won't complain, but you could potentially get a runtime error. This is highly unlikely unless err is null/undefined, but it's still not perfectly type safe since you could forget the ? and get runtime errors in theory. The issue here is you're telling the compiler you know what the error is when in fact the error could be literally anything (which is the motivation to automatically cast errors as unknown).
You could also do catch (err: any) but you won't get any type hints for the codes and you are still subject to the same issues if you forget the use the safe accessor on the code property. There's not a particularly easy way around this since you cannot simply use safe accessor on unknown types like this: if (err?.code === 'ENOENT') return;. I'm not quite sure why and maybe they'll add this in a later realease, but either way, my favorite way to handle these fs errors is to write a typeguard helper function like so:
BEST method
function isErrnoException(e: unknown): e is NodeJS.ErrnoException {
if ('code' in (e as any)) return true;
else return false;
}
And then your catch block like this:
try {
await fs.readFile(file);
} catch (err) {
// writing err.code here after the typeguard call satisfies the compiler and is SAFE because we already checked the member exists in the guard function.
if (isErrnoException(err) && err.code === 'ENOENT') return;
else throw err;
}
This checks at least the error object is ErrnoException-like in that it has a code property. You could get more specific and test that ALL of the ErrnoException properties exist to really make sure it's an ErrnoException.
In JavaScript/TypeScript you can throw anything, not only errors. In theory it could be anything in the catch block. If you want to prevent the type error it could make sense to check if the unknown value is a system error before checking the code.
if (err instanceof SystemError && err.code === 'ENOENT') {
// file not found
}
cast using Record<string, unknown> for the type that you don't know about... so it will be:
const mysteryObject = (unknownObject as Record<string, unknown>)
this message below really helped me and resolved the issue also:
in typescript
you can add err : any
ex:
runInitValidation(_bean: any, _oldVal: any, newVal: any) {
const { validators } = this.props;
if (!validators) return;
try {
for (let i = 0; i < validators.length; i++) {
validators[i].validate(newVal);
}
} catch (err: any) {
this.errorMessage = err.message;
}
}

I am getting issue in mongoose calback in custom validation

I am using express-validator and have made one custom validation with mongoose database find, now I can't send withMessage. Here is my code althought process stop at Error but it does not show message in Json, but when user created it is show all.
body('mobile', 'Mobile number is required')
.custom((value, {req, loc, path}) => {
User.countDocuments({mobile: value}, function (err, c) {
if (err) {
console.log('An error happen in db')
}
if (c > 0) {
throw new Error('Mobile already exists finally')
} else {
return value
}
})
return value
})
.withMessage('Mobile already exists'),
Following is log of console
functions: Beginning execution of "app"
> events.js:298
> throw er; // Unhandled 'error' event
> ^
>
> Error: Mobile already exists finally
> at /Users/kamal/Documents/personal/asghar/codes/functions/app/controllers/users_controller.js:117:23
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4849:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4849:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4872:21
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/query.js:4379:11
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/kareem/index.js:135:16
> at processTicksAndRejections (internal/process/task_queues.js:79:11)
> Emitted 'error' event on Function instance at:
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4851:13
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
> [... lines matching original stack trace ...]
I need to add if condition near return value but problem is call back does not bring me value of c here, in above it is comign correct and even stop due to Error raising, but I don't want to raise error instead want to go further withMessage('Mobile already exists') I am sure doing mistake in callback. Please suggest solution
This should work
body("mobile").custom(value => {
return User.countDocuments({ mobile: value })
.then(count => {
if (count > 0) return Promise.reject("Mobile number already exists");
})
.catch(error => console.error(error.message));
});
From express-validator
documentation
Custom validators may return Promises to indicate an async validation
(which will be awaited upon), or throw any value/reject a promise to
use a custom error message. Note: if your custom validator returns a
promise, it must reject to indicate that the field is invalid.
After playing and seeing many many documentation I solved this by doing following, in my User model I put following code, as my model is actualy mongoose module.
UserSchema.statics = {
isValidMobile(mobile) {
console.log(` Searching obile ${mobile} number `)
return this.findOne({mobile: mobile}).then((result) => {
if (result) throw new Error('Mobile Number already exists')
})
},
}
Now in my validation I put following line instead of above lines.
body('mobile', 'Mobile number is required')
.custom((val) => User.isValidMobile(val))
It worked, and I am getting proper messages with whole JSON, as it seem custom require proper true/false reply so my method is replying just true or false and it is working fine with correct message, but the message is being used came from User model class not from validation, but that works. Thank you for responses.

knex issue whereNotExists

I am having some issues with a Knex route for PostgreSQL. I am trying to insert into a database but only when the item is not in the database already. I am trying to use where not exists but it doesn't seem to be doing what I want it to. I appreciate any help you can give me.
Thank you!
app.post('/addcart', (req,res)=>{
const{customer_id, product_id,item_quantity}=req.body;
db('shopping_carts')
.insert({
customer_id:customer_id,
product_id:product_id,
item_quantity:item_quantity
})
.whereNotExists(db.select('*').from('shopping_carts').where('product_id',product_id))
.then(item=>{
console.log(item)
res.json(item)
})
.catch((err)=>{
if(err.column === 'customer_id'){
res.status(400).json({message:err})
console.log('test')
}else{
res.status(500).json({message:err})
// console.log(err.name);
}
})
})
You can't combine a whereNotExists query with an insert query, they don't support this due to it's complexity (and per #mikael, most db's don't support this). So knex ignores the whereNotExists call after the insert in your method chain.
You need to check for existence first, and then do the insert, via separate calls.
You could also write a raw query. Here's an example, it's not pretty:
https://github.com/tgriesser/knex/commit/e74f43cfe57ab27b02250948f8706d16c5d821b8
However, you will run into concurrency/lock issues when trying to do this. You're much better off making use of a unique key and letting the DB reject the insert. Then you can catch it:
.catch((err) => {
if (err.code === 23505) { res.status(500).json({message: 'duplicate'});
}
Edit, more info if you're curious. There's a very long thread on the topic here:
https://github.com/tgriesser/knex/issues/871
Edit: thread from #mikael regarding DB's and insert-where:
https://github.com/tgriesser/knex/issues/871

How should my node/express RESTful API should return database errors to the clients?

Right now if there is an error, such as duplicate entry, I return it like this res.status(500).send(err);
Showing the client this kind of things:
{
"code": "ER_DUP_ENTRY",
"errno": 1062,
"sqlMessage": "Duplicate entry 'name#mail.com' for key 'user_table_email_unique'",
"sqlState": "23000",
"index": 0,
"sql": "update `user_table` set `email` = 'name#mail.com' where `id` = 3"
}
What is the standard way of doing this? I want the client to know what kind of error is but not, for example, the name of my tables
without seeing your code structure it's difficult to help, but if you have your query in a function that returns to your main script you could catch there, process the error and then throw a succinct and non-data exposing error back to the calling function.
// This is pseudocode
const updateEmail = (db, email, id) => db.query('query...')
.then(data => /* process things */)
.catch(err => {
if (err.errno === 1062) {
throw new Error('Email already exists');
}
});
I'm not certain that 500 is the correct code to use in this particular case either, as it isn't a server error per se. That would be a preference thing though depending upon how your other requests are structured.

Returning response from Mongoose promise

Followup from this question > Stopping response if document isn't found since it was recommended I use Promise.
So basic premise, I want node to return "Can't find ID" message if we can't find the id in our database.
v1.post("/", function(req, res) {
// If the project_id isn't provided, return with an error.
if ( !("project_id" in req.body) ) {
return res.send("You need to provide Project ID");
}
// Check if the Project ID is in the file.
helper.documentExists( ProjectsData, {project_id: req.body.project_id} )
.then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
var gameDataObj = req.body;
GameData.addGameId(gameDataObj, function (err, doc) {
if (err) {
if (err.name == "ValidationError") {
return res.send("Please send all the required details.");
}
throw err;
};
res.json(doc);
})
};
});
});
And helper.documentExists
module.exports = {
documentExists: function(collection, query) {
return collection.count( query ).exec();
},
};
But the script continues to run after this and prints the "required data not found".
Output:
required data not found
1
I am using native ES6 Promises.
var mongoose = require("mongoose");
mongoose.Promise = global.Promise;
EDIT: Included the entire get route. (will fix those throw err later)
#######POINT 1#########
ProjectsData.count( {project_id: req.body.project_id} )
.then(function(c) {
#######POINT 3#########
if ( c == 0 ) {
console.log("1");
return res.send("The provided Project Id does not exist in our database.");
console.log("2");
}
});
#######POINT 2#########
//some other logic
console.log("required data not found");
Following async workflow: after POINT 1, the promise is created and your handler is attached. Now POINT 2 will continue, while (at some future clock the promise is resolved and you reach POINT 3.
With my limited understanding of your workflow/purpose I'd say simply put POINT 2 code in the else{} of the if at POINT 3 (as you rightly guessed in the comments).
EDIT: thanks to #jfriend00 for pointing out a serious mistake in the previous version of my answer.
Your code essentially results in this:
ProjectsData.count().then(...);
console.log("required data not found");
So, of course the second console.log() is going to run and print. Nothing that happens in the .then() handler runs until long after the console.log() has already run. And, even then, it can't stop other code from running. Promises don't make the interpreter "wait". They just provide structure for you to coordinate your asynchronous operations.
If you want to branch with promises, then you have to branch inside the .then() handler, not after it.
You don't show enough of the rest of what you're doing to know how to recommend a complete solution. We need to see the rest of your request in order to help you with the proper branching based on asynchronous results.
You probably need something like this:
ProjectsData.count( {project_id: req.body.project_id} ).then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
// put other logic here
}
}).catch(function(err) {
// handle error here
});

Resources