Mongoose: Unhandled promise rejection - node.js

I know there are other posts with similar issues, but none of the suggestions I've tried have worked.
The following works if the _id is valid, but throws an unhandled promise rejection error if it isn't:
const Movie = mongoose.model(`Movie`, movieSchema);
router.get(`/api/movies/:id`, async (req, res) => {
let movie = await Movie.findById(req.params.id);
if(!movie) {
res.status(404).send(`Movie with given ID not found.`);
return;
};
});
Per the docs, it looks like findById() is supposed to return null if the id can't be found, so I'm not sure what the issue is. Do I need to put a catch block somewhere and put the 404 in there? I've tried putting it everywhere I can think to.

As per the Mongoose documentation...
Model.findById()
Returns:
«Query»
Looking into the Query API, when used like a Promise, it will invoke the Query.prototype.then() implementation
Executes the query returning a Promise which will be resolved with either the doc(s) or rejected with the error.
To use this, you would need something like
try {
const movie = await Movie.findById(req.params.id)
// do stuff with movie
} catch (err) {
res.sendStatus(404)
}

use .then() and .catch() will sort your issue.

Related

Using async-await to execute a task AFTER dropping a collection from mongodb database

I am trying to use async await to execute an http request before executing some other code.
More precisely, I would like to drop a collection in my mongodb database, before executing some others tasks. Here's what I did:
app.component.ts:
async deleteRiskRatingData2() {
await this.saveInformationService
.deleteRiskRatingInformation()
.subscribe((data: string) => {
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
});
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
save-information.service.ts
deleteRiskRatingInformation() {
console.log('INIDE deleteRiskRatingInformation INSIDE SAVE-INFORMATION.SERVICE');
return this.http.get(`${this.uri}/dropRiskRatingCollection`);
}
In the backend:
server.js
router.route('/dropRiskRatingCollection').get((req, res) => {
RiskRating.remove({},(err) => {
if (err)
console.log(err);
else
res.json("Risk Rating Collection has been dropped!");
});
});
And this is what happens:
I though my implementation of Async/Await should allow me to execute the:
console.log('TASKS TO BE EXECUTED AFTER DROPPING COLLECTION');
After the dropping of the collection request has been executed. But that didn't happen as you see. And I really don't understand why.
Any idea why is this happening? Is my logic flawed somewhere? And how can I achieve my goal?
Thank you!
async-await work only with Promises. You're try them with Observables. That won't work. Observables have an API that let's you convert them into Promises though. You can call a toPromise method on them in order to do that.
Try this:
async deleteRiskRatingData2() {
const data = await this.saveInformationService.deleteRiskRatingInformation().toPromise();
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
NOTE: It's fine if you're trying this just for the sake of testing it. But I think you should not really switch back to promises just to use async-await, to make your code look synchronous.

Cannot read property 'catch' of undefined

I am trying to use Argon2 encryption in Node, but when I try to encrypt a string, I get this error:
Cannot read property 'catch' of undefined
I have tried handling the errors from the promise returned by the argon2.hash function, but it still does not work.
This is my code so far:
argon2.hash('password', {type: argon2.argon2id})
.then(hash => {
// do something with the hash
}).catch(err => {
// Handle the error
});
Could anyone please help me with fixing this error?
In my case I got that error message because I
a) spied on some async method
spyOn(sut,'myAsyncMethod')
b) later appended .catch() to the original method call and forgot to extend the spy to return a value/promise.
Returning a promise from the spy solved my issue:
spyOn(sut,'myAsyncMethod').and.returnValue(new Promise(resolve=>resolve()));
It throws an exception, it does not return a promise. As such, there is no promise object on which the then(…).catch(…) methods could be invoked.
To catch it, you would need an actual try/catch block
from argon2 github page, you should do this:
const argon2 = require('argon2');
try {
const hash = await argon2.hash("password");
} catch (err) {
//...
}
Try the following instead:
argon2.hash('password', {type: argon2.argon2id})
.then(hash => {
// do something with the hash
}, err => {
// Handle the error
});
The second parameter to a then clause is the onError handler.

Monk - Where goes the data after a query?

I have started using Monk today, and there are a few things that I don't really get, and documentation is too light.
First here is the code:
const movieToProcess = movieCollection.findOne({ link: videoURL }).then((doc) => {
console.log(doc)
console.log("BLABLA")
});
console.log("CURSOR", typeof(movieToProcess))
First thing, I don't understand why the two console.log inside the promise .then() are not displaying, is that normal? If so, why?
And if this is not normal that the console.logs don't work, why is that?
And finally, in then, how can I get the return value of findOne()?
Bonus: Is there another function than findOne() to check if the value exist in the database?
I apologise for these questions, but there are not that much documentation for Monk.
A few things:
In your example you are setting movieToProcess to the value of movieCollection.findOne() while also calling .then() on it.
in your .then, doc is the return value of findOne()
ALSO, referring to #Geert-Jan's comment, the promise is probably being rejected and you aren't catching it.
Try this:
movieCollection.findOne({ link: videoURL })
.then((doc) => {
console.log(doc)
console.log("BLABLA")
})
.catch((err) => {
console.log(err)
})
I'll also add that findOne() does not return a cursor, it returns a document.

How to do proper error handling with async/await

I'm writing an API where I'm having a bit of trouble with the error handling. What I'm unsure about is whether the first code snippet is sufficient or if I should mix it with promises as in the second code snippet. Any help would be much appreciated!
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = await models.user.findById(decoded.userId);
user.active = true;
await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
Second code snippet:
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = models.user.findById(decoded.userId).then(() => {
}).catch((error) => {
});
user.active = true;
await user.save().then(() => {
}).catch((error) => {
})
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
The answer is: it depends.
Catch every error
Makes sense if you want to react differently on every error.
e.g.:
try {
let decoded;
try {
decoded = jwt.verify(req.params.token, config.keys.secret);
} catch (error) {
return response
.status(401)
.json({ error: 'Unauthorized..' });
}
...
However, the code can get quite messy, and you'd want to split the error handling a bit differently (e.g.: do the JWT validation on some pre request hook and allow only valid requests to the handlers and/or do the findById and save part in a service, and throw once per operation).
You might want to throw a 404 if no entity was found with the given ID.
Catch all at once
If you want to react in the same way if a) or b) or c) goes wrong, then the first example looks just fine.
a) var decoded = jwt.verify(req.params.token, config.keys.secret);
b) var user = await models.user.findById(decoded.userId);
user.active = true;
c) await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
I read some articles that suggested the need of a try/catch block for each request. Is there any truth to that?
No, that is not required. try/catch with await works conceptually like try/catch works with regular synchronous exceptions. If you just want to handle all errors in one place and want all your code to just abort to one error handler no matter where the error occurs and don't need to catch one specific error so you can do something special for that particular error, then a single try/catch is all you need.
But, if you need to handle one particular error specifically, perhaps even allowing the rest of the code to continue, then you may need a more local error handler which can be either a local try/catch or a .catch() on the local asynchronous operation that returns a promise.
or if I should mix it with promises as in the second code snippet.
The phrasing of this suggests that you may not quite understand what is going on with await because promises are involved in both your code blocks.
In both your code blocks models.user.findById(decoded.userId); returns a promise. You have two ways you can use that promise.
You can use await with it to "pause" the internal execution of the function until that promise resolves or rejects.
You can use .then() or .catch() to see when the promise resolves or rejects.
Both are using the promise returns from your models.user.findById(decoded.userId); function call. So, your phrasing would have been better to say "or if I should use a local .catch() handler on a specific promise rather than catching all the rejections in one place.
Doing this:
// skip second async operation if there's an error in the first one
async function someFunc() {
try {
let a = await someFunc():
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// skip second async operation if there's an error in the first one
function someFunc() {
return someFunc().then(someFunc2).catch(e => "");
}
No matter which async function rejects, the same error handler is applied. If the first one rejects, the second one is not executed as flow goes directly to the error handler. This is perfectly fine IF that's how you want the flow to go when there's an error in the first asynchronous operation.
But, suppose you wanted an error in the first function to be turned into a default value so that the second asynchronous operation is always executed. Then, this flow of control would not be able to accomplish that. Instead, you'd have to capture the first error right at the source so you could supply the default value and continue processing with the second asynchronous operation:
// always run second async operation, supply default value if error in the first
async function someFunc() {
let a;
try {
a = await someFunc():
} catch(e) {
a = myDefaultValue;
}
try {
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// always run second async operation, supply default value if error in the first
function someFunc() {
return someFunc()
.catch(err => myDefaultValue)
.then(someFunc2)
.catch(e => "");
}
Note: This is an example that never rejects the promise that someFunc() returns, but rather supplies a default value (empty string in this example) rather than reject to show you the different ways of handling errors in this function. That is certainly not required. In many cases, just returning the rejected promise is the right thing and that caller can then decide what to do with the rejection error.

async creating folder in nodejs

I tryed to make simple script that create a folder if it not exsists. I readed some articles and maked logic like this:
debug("before async");
(async () => {
if(!fs.existsSync(outputPath)){
debug("Folder not exsists! path: "+outputPath)
try{
return await fs.mkdir(outputPath)
}catch(err){
debug(err)
}
}
res.send('<h1>Hello world!</h1>')
})()
I got an error:
(node:27611) [DEP0013] DeprecationWarning: Calling an asynchronous function without callback is deprecated.
Ok. I figured a little and remind that guy from stackoverflow tell me make callback functon as promisify. I tryed to make it:
const mkdir = util.promisify(fs.mkdir);
debug("Before async");
(async () => {
if(!fs.existsSync(outputPath)){
debug("Folder not exsists! path: "+outputPath)
await Promise.all(mkdir(outputPath))
}
res.send('<h1>Hello world!</h1>')
})()
But I got other error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): TypeError: undefined is not a function
How was I supposed to make all? If you know any guides that can help me to understand about asynchronous functionality - that will be great. Thanks!
btw, both ways folder was created. but with errors...
Node 11.x.x and later
await fs.promises.mkdir( '/path/mydir' );
Promise.all is for running an array of promises. In your case, you can just do this:
await mkdir(outputPath)
Depending on your needs, you could potentially do something like this:
await Promise.all([mkdir(path1), mkdir(path2), mkdir(path3)])
I would recommend becoming familiar with callbacks and promises before jumping into async/await.
You cant/dont need to use Promise.all as you only handling one promise anyway, just do
await mkdir(outputPath)
Besides that you really should add some error handling to your code.
Asynchronously checking to see if a folder exists and then creating one if it doesn't. Works from Node.js ^10.20.1.
const fsPromises = require("fs").promises;
async function createDir(dir) {
try {
await fsPromises.access(dir, fs.constants.F_OK);
} catch (e) {
await fsPromises.mkdir(dir);
}
}

Resources