How to throw custom error in async callout - javascript

I'm making a couple async API callouts and may throw a custom error depending on the outcome.
I'm deleting Objects from S3.
try {
await s3.deleteObject(bucketParams);
//S3 API doesn't provide resp on if obj successfully deleted. Therefore, check that it doesn't exist afterwards to verify instead
const err = await s3.headObject(bucketParams);
if (err & err.code === 'NotFound') return { code:200, message:`${key} successfully deleted` }
throw `Error deleting ${key}`;
} catch (error) {
throw new Error(500, error);
}
So the main catch can handle any exceptions thrown by those couple API callouts (error with network, bad key, bad authorization, etc..)
However, if the object didn't actually get deleted (so if it still exists)..I throw a custom error to get reported back to end user.
My question is if there's a better pattern to just throwing a custom error in your try and then having it get slurped up by your catch.
Thanks!

You can do a lot to be honest, you just need to work out what you want to capture. Heres a basic example. If you were using TypeScript you would get more power and type safety
class S3ObjectNotFound extends Error {
// Add other attributes you might like
code
constructor(message) {
super(message)
this.name = 's3/object-id-not-found'
this.code = 404
}
}
const someFunction = async (bucketParmas, key) => {
try {
try {
await s3.deleteObject(bucketParams)
} catch (s3Error) {
throw new S3ObjectNotFound(`Object with id ${bucketParmas} was not found`)
}
const err = await s3.headObject(bucketParams)
if (err && err.code === 'NotFound') {
return { code: 200, message: `${key} successfully deleted` }
}
throw AnotherErrorYouCouldMake()
} catch (error) {
throw new Error(500, error)
}
}
someFunction({...YourParams}, 'YourKey')

Related

throw statement is ignored within Nextjs middleware helper

I am writing middleware code for setting up authentication and token validation in my nextjs app. I am using throw instead of returning value from the helper function.
But throw (last statement) is not working in the following code:
async function validate(request: NextRequest) {
const tokenid = "test";
if (tokenid) {
try {
// in case of error
throw new Error("test");
} catch (error) {
console.error("validate: ", error);
}
}
throw new Error("tokenId validation failed");
}
This function is called within another function and where the last statement is simply ignored.
export async function googleAuthValidate(request: NextRequest) {
let response = NextResponse.next();
try {
validate(request);
console.log("throw didnt go into catch");
} catch (error) {
console.error(error);
response = NextResponse.rewrite("/unauthorized");
} finally {
return response;
}
}
I have created a repository for this issue for easy reproduction: repo

Express: new Error() is not working outside of server.js/index.js

I created a simple custom exception based on this link as follows:
function userErr(message) {
logger.info("reach user error function")
const error = new Error(message);
error.type = "userErr";
return error;
}
function failureErr(message) {
logger.info("reach failure error function")
const error = new Error(message);
error.type = "failureErr";
return error;
}
The expected behaviour is Express will throw this error to the custom error handler middleware at the end of the server.js file:
app.use((error, req, res, next) => {
console.log("Error Handling Middleware: " + error.message);
if (error.type === "userErr") {
res.status(400).json({
status: "error",
statusCode: "400",
message: error.message
});
} else if (error.type === "failureErr") {
res.status(500).json({
status: "fail",
statusCode: "500",
message: error.message
});
} else {
res.status(500).json({
status: "error",
statusCode: "500",
message: error.message
});
}
});
app.listen(8080, () => {
console.log("listening on 8080...");
}); //the server object listens on port 8080
When I put these functions above the routes (app.get, app.post, etc...) in the server.js file (this should be index.js for most people), it works fine. But when I start stripping out these routes and put them into a route file in the routes folder instead, the traffic does come to the route correctly, but whenever an error occurs and executes the function above, JavaScript threw an error at the 2nd line, which is:
const error = new Error(message);
for example:
const error = new Error(message);
^
Error: [object Object]
at new failureErr (file:///.../src/server.js:89:17)
at file:///.../src/routes/testRoutes.js:104:21 {
type: 'failureErr'
Which is weird! I have tried putting these functions into a separate file and importing them into the route file, but it still shows the same error.
I mimicked the folder into the codesandbox (I couldn't manage to make it run) so you can understand the context. The index.js by itself is the one that works, while index2.js with the routers folder is the one that failed.
Appreciate any feedback on the questions, or any ideas on where I did something wrong
The code you show in your sandbox is doing this throw new userError("It's an user error");, but userError() is not properly set up as a constructor and thus can't be called with new. This is the code in your sandbox:
app.get("/api/v1/test", (req, res, next) => {
try {
const userErr = req.headers.userErr;
console.log(userErr);
if (userErr === "true") {
throw new userError("It's an user error");
} else {
throw new failureError("It's an failure error");
}
} catch (e) {
next(e);
}
});
The code you show has not made either userError() or failureError() into classes or constructors. They are merely functions. Therefore, they can't be called with new.
If you want to make them into classes, then do something like this:
class userError extends Error {
constructor(message) {
super(message);
console.log("reach user error function");
this.type = "userErr";
}
}
Note, this is the more modern way of doing this than what is shown in the article you reference (which is apparently old).
Or, you can just use what you have as functions:
app.get("/api/v1/test", (req, res, next) => {
try {
const userErr = req.headers.userErr;
console.log(userErr);
if (userErr === "true") {
next(userError("It's an user error"));
return;
} else {
next(failureError("It's an failure error"));
return;
}
res.send("ok");
} catch (e) {
next(e);
}
});

Combing async function and sql query

I'm new to async functions and I'm struggling getting it do work with db queries...
Here's what I have:
fetch: async (id) => {
if (id!=='something') {
return await db.getConnection().query("SELECT user FROM `table` WHERE id='" + id + "'", function (err, result) {
if (err) { throw err }
else {
console.log(result[0].user);
return result[0].user;
}
});
}
throw new Error('Failed fetching installation');
}
This results in getting an error: Cannot read property 'user' of undefined , but after this error, the console.log comes in indicating result[0] is actually defined.
So my guess is that it's not waiting for the result or something like that (even though it's not hitting the throw new Error).
Anyway, I'm sure this is just me not getting this right... as said, I'm only just getting started with these kinds of async behaviors.
As mentioned above you are mixing promises and callbacks. I am unsure if my example below is possible with your database library, but I think if it is, its a much cleaner approach.
const fetch = async (id) => {
if (id !== 'something') {
try {
const query = await db
.getConnection()
.query("SELECT user FROM `table` WHERE id='" + id + "'")
return query[0].user
} catch (error) {
console.log(error)
throw new Error(`Execution of query failed with error: ${error}`)
}
}
throw new Error('Failed fetching installation')
}
const user = fetch('your-id') // (prefix fetch with await if not at top level)

Mongoose and multiple save error handling

I'm using mongoose + express to build a simple MERN app.
I need to create multiple documents and save them, but I need to catch all errors.
I'm using this code and it works, but I'd like to handle all errors at once, not repeat the same code multiple times.
If I use try...catch block and remove the callback error handler, I obtain UnhandledPromiseRejectionWarning.
model.save((err, doc) => {
if (err) return console.error(`ERR ${err.message}`);
});
I've tried this:
export const init = async () => {
try {
const newDoc = new MyModel({ test: 'test'});
const savedDoc = await newDoc.save();
console.log('All done :)');
} catch (err) {
console.log('Error');
res.status(400).send(err);
}
}
But I can't catch the error: in debug mode, the program never enter the catch block and I obtain, in case of error for example:
UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key error collection
Any suggestion?
model.save()
.then(success => {
if(!success) {
// Handle your error
}
// Success
return model2.save();
})
.then(success2 => {
})
// etc..
.catch(err => {
// Handle your error
});
try{
const savedModel = await model.save();
console.log("Model created successfully");
res.status(200).send("Model created successfully");
}catch (err){
console.log(err);
res.status(400).send(err);
}

How to catch a Typeorm transaction error in NestJs

I have a server side written in node.js and nestJs, querying with typeorm.
I'm trying to wrap some query's to the database with transaction as suggested here with some changes inspired by typeorm's docs, like this:
getManager().transaction(async transactionalEntityManager => {
transactionalEntityManager.save<Entity>(newEntity)
transactionalEntityManager.save<Entity1>(newEntity1)
});
The transaction works well and rollback the database if there was an error.
tested this way:
getManager().transaction(async transactionalEntityManager => {
transactionalEntityManager.save<Entity>(newEntity)
throw 'There is an error'
transactionalEntityManager.save<Entity1>(newEntity1)
});
The execution of the transaction is inside a graphQL Mutation, so I should return an error to the client if something went wrong, the problem is that I can't catch the errors from the transaction.
Tried doing this:
#Mutation(returns => Entity)
async create(): Promise<Entity> {
let entity = null;
let error = null;
getManager().transaction(async transactionalEntityManager => {
try {
entity = await transactionalEntityManager.save<Entity>(newEntity)
await transactionalEntityManager.save<Entity1>(newEntity1);
} catch (err) {
error = err
}
})
if (error) {
return error
}
return entity
}
when I throw error I catch it successfully, but when a real error occurs I can console.log() it in the server but it never reaches to return to the client.
You are not awaiting your transaction to finish thus, the exception can be thrown after your function call end and you don't get the exception.
Just await your transaction and it should be fine.
await getManager().transaction(async transactionalEntityManager => {
...
throw 'ERROR THAT SHOULD BE CATCHED'
}
Also it returns the result of the inner function so it can be useful.
const ok = await getManager().transaction(async transactionalEntityManager => {
await transactionalEntityManager.save<Entity>(newEntity)
await transactionalEntityManager.save<Entity>(newEntity2)
return 'OK'
}

Categories