I'm refactoring my nodejs code. I'm implementing transactions using node-postgres. My code now looks like this:
const controller = async (req, res, next) => {
const client = await pool.connect()
try {
// get the user ID out of the params in the URL
const { user_id } = req.params
let query, values, result
try {
await client.query('BEGIN')
} catch (err) {
throw new CustomError(err)
}
try {
query = 'QUERY_1'
values = ['VALUES_1']
result = await client.query(query, values)
} catch (err) {
throw new CustomError(err)
}
// handle some stuff
try {
query = 'QUERY_2'
values = ['VALUES_2']
result = await client.query(query, values)
} catch (err) {
throw new CustomError(err)
}
// handle some more stuff
try {
await client.query('COMMIT')
} catch (err) {
throw new CustomError(err)
}
return res.status(200).json({ response: 'ok' })
} catch (err) {
await client.query('ROLLBACK')
return next(new CustomHandleError(400, 'something_went_wrong'))
} finally {
client.release()
}
}
This code works, but I have some questions. I'm a beginner at nodejs.
1) In my 'finally' block, I release the client back to the pool. But when everything is OK, I return the response in the 'try' block. Online I read that the 'finally' block is ALWAYS executed, so is it OK to return a (good) response in the try block and releasing the client in the finally block?
2) Is it OK (or is it anti-pattern) to nest multiple try catch blocks. The reason is that the node-postgres throws errors but I want to return all errors to a custom error handler, so I catch those errors first and then throw them again in my CustomError handler.
Thanks in advance!
You can significant simplify your try/catch handling since all the inner catch blocks all do the same thing and are not necessary:
const controller = async (req, res, next) => {
const client = await pool.connect()
try {
// get the user ID out of the params in the URL
const { user_id } = req.params
let query, values, result;
await client.query('BEGIN');
query = 'QUERY_1'
values = ['VALUES_1']
result = await client.query(query, values)
// handle some stuff
query = 'QUERY_2'
values = ['VALUES_2']
result = await client.query(query, values)
// handle some more stuff
await client.query('COMMIT')
return res.status(200).json({ response: 'ok' })
} catch (err) {
await client.query('ROLLBACK')
return next(new CustomHandleError(400, 'something_went_wrong'))
} finally {
client.release()
}
}
Then, to your questions:
1) In my 'finally' block, I release the client back to the pool. But
when everything is OK, I return the response in the 'try' block.
Online I read that the 'finally' block is ALWAYS executed, so is it OK
to return a (good) response in the try block and releasing the client
in the finally block?
Yes, this is a good use of finally.
2) Is it OK (or is it anti-pattern) to nest multiple try catch blocks. The reason is that the node-postgres throws errors but I want to return all errors to a custom error handler, so I catch those errors first and then throw them again in my CustomError handler.
It's OK (not an anti-pattern when it achieves a specific goal), but in this case it is not necessary because all your inner catch() blocks all do the same thing and are all just caught by your outer catch block so you can just keep the outer catch and get rid of all the inner ones. All your await statements will just go directly to your outer catch if they reject in my code above which all you were doing anyway with all your inner catch statements so they were redundant.
Some reasons for needing the inner catch are:
You want to create a custom error (that is different for each async operation) that you will actually use in the final result of the function.
You want to "handle" an error locally and continue processing down a different code path that is not just an immediate error return. For example, you attempt to load a config file, catch the error and just proceed with the rest of the code in your function with defaults if the config file is not present.
1) Yes, this is good practice.
2) I don't think it's automatically an anti-pattern, but I'd avoid it if I can find a cleaner way to do it.
Rule of thumb from Robert C. Martin's suggestion from his book 'Clean Code':
if the keyword 'try' exists in a function, it should be the very first word in the function and that there should be nothing after the catch/finally blocks.
If you have a chance, then avoid nesting try-catches.
Blocks out into separate functions
Related
class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.
I have a function like this:
async getPatient(patientId: string): Promise<PatientDTO> {
const patient = await PatientDAO.getPatients({ id: patientId })
if (patient.length === 0) {
throw new NotFoundError("Patient Not Found!")
}
return patient[0]
}
But I got an error
UnhandledPromiseRejectionWarning: Error: Patient Not Found!
This happened cause I have used async function. How can I make this code running properly?
In order to manage errors in an async function, you have to use a try/catch block:
async getPatient(patientId: string): Promise<PatientDTO> {
try {
const patient = await PatientDAO.getPatients({ id: patientId })
return patient[0]
} catch (error) {
// Do whatever you may want with error
throw error;
}
}
I should mention, that if you simply want to throw the error thats received from getPatients theres no need for a try/catch block at all. Its only needed if you wish to modify the error or perform an extra action according to the error that was thrown.
You have 2 options:
First one is try/catch block with await keyword. Please notice that await has to be used in async function.
try {
const patient = await getPatient(foo);
// handle your data here
} catch(e) {
// error handling here
}
Second one is catch function
getPatient(foo)
.then(patient => {
// handle your data here
}).catch(error => {
// error handling here
});
I have a post route which calls a service and has a .catch handler to handle any errors, also the code itself has try - catch block. But when I'm trying to call another service only of first service callback value is not desired, it shows following errors. Eg. Await cannot be used outside async, or when it goes to condition 2, app crashes. Here's the structure-
router.post('/Students', async(req,res) => {
try{
...
studentService(req.body). then ((res)=>{
....// if res.body == 'Student'
.....
res.send(res.body)
}
})
.catch(error){
....
}
catch(err){
....
}
}
module.exports = router
Now how can I check that if res.body!='Student' or the studentService fails, then go to second block which calls another service and has it's own error handler.(Note- It executes only if first one is false or desired value not obtained)
Not sure where and how to place it.
I guess asynce in your example code is a typo.
Inside an async function you can use await rather than .then().
Also, it's probably best if you don't overwrite your route's res with the output from your studentService.
And, in a route you must do something in all cases. Send a result, throw an error, send an error, whatever. The else side of your if doesn't do anything in your sample.
You can throw an error by calling express's next() with a parameter.
So try this.
const createError = require('http-errors')
...
router.post('/Students', async(req, res, next) => {
try {
...
const student = await studentService(req.body)
if (student.body === 'Student') {
...
return res.send(student.body)
}
catch (error) {
return next(error)
}
try {
const something = await someOtherService(req.body)
if (something.whatever === 'Underpaid Adjunct Faculty') {
return res.send(something.body)
}
} catch (error) {
return next(error)
}
return next(createError(400, 'Got an error'))
}
module.exports = router
router.get('/',ensureLogin,async (req,res)=>{
try{
let result = await Mock.find();
result.forEach(async (e)=>{
if(e.attemptedBy.includes(req.user._id)){
let a_Mock = await User.findOne({_id:req.user._id,"attemptedMock.setNo":e.setNo},{attemptedMock:1});
e.status="Attempted";// not accessible on template as res.render executes first
e.marks = a_Mock.attemptedMock[0].totalMarks; //not accessible
console.log(a_Mock.attemptedMock[0].totalMarks);
}
else{
e.status = "Unattempted";//it is accessible
console.log('else block')
}
})
console.log("second log");
res.render('dashboard',{mocks:result});//I want this code to be executed when the "result" is updated by "forEach" block
}catch(e){
console.log(e);
}
})
Console logs as : 1. "else block" 2. "second log" 3."res.render executed" 4. "total marks", that concludes that res.render executed before "if block's" statement "e.status='Attempted'" so it is not accessible on template page. Please tell me how to refactor in a proper way.
If you are trying to do your loop one at a time and then call res.render() after the loop is done, then the easiest way to do that is to use a regular for loop as it will actually "wait" for the await. .forEach() is not promise-aware so, while the callback itself will wait for the await before it finishes, .forEach() itself will not and it will run the entire loop and then the next line after it will run and then each individual callback will finish sometime later. That will cause you to call res.render() before any of the .forEach() callbacks are done. For this reason, you pretty much never want to use .forEach() with promises as it's just not promise-aware. Instead, use a regular for loop like this:
router.get('/', ensureLogin, async (req,res) => {
try {
let result = await Mock.find();
for (let e of result) {
if (e.attemptedBy.includes(req.user._id)) {
let a_Mock = await User.findOne({_id:req.user._id,"attemptedMock.setNo":e.setNo},{attemptedMock:1});
e.status = "Attempted";
e.marks = a_Mock.attemptedMock[0].totalMarks; //not accessible
} else {
e.status = "Unattempted"; //it is accessible
}
}
console.log("second log");
res.render('dashboard', {mocks:result});
} catch(e) {
console.log(e);
res.sendStatus(500);
}
});
Note also that this code sends an error status in the catch block. You need to always send some response to the incoming http request.
inside for each, you can wrap everything in function.
refernece :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all#:~:text=The%20Promise.,input%20iterable%20contains%20no%20promises.
processE = async(e)=>{
try{
if(e){
await something
e.something 1= somevalue
}else{
e.something 2= somevalue
}
return false
}catch(err){
//you can also throw depends on how u handle your err and type of promise.something you use
return err
}
}
let promises = []
e.forEach( (e)=>{
promises.push(processE(e))
})
/// Promise.something you can take a look at reference and use ethe one behavior that you prefer.
const result = await Promise.all(promises)
//render here
How to handle multiple calls to the same function when its returning nothing. I need to wait untill all calls are finished so i can call another function.
For now I'm using Promise.all() but it doesn't seem right:
Promise.all(table_statements.map(i => insertValues(i)))
.then(function(result) {
readNodeData(session, nodes);
})
.catch(function() {
console.log(err);
})
function insertValues(statement) {
return new Promise((res, rej) => {
database.query(statement, function (err, result) {
if (err) {
rej(err)
}
else{
console.log("Daten in Tabelle geschrieben")
res(); // basically returning nothing
}
});
});
}
This writes data to a database in multiple statements, i need to wait untill all are finished.
Is this actually the "right" way to do it? I mean... it works, but i have the feeling it's not how you are supposed to do it.
Using Promise.all for your case is a good call, since it returns a Promise, when all the promises passed as an iterable are resolved. See the docs.
However, for brevity and readability, try converting your insertValues into async-await function as follows. This tutorial would be a great place to start learning about async functions in JavaScript.
// async insertValues function - for re-usability (and perhaps easy unit testing),
// I've passed the database as an argument to the function
async function insertValues(database, statement) {
try {
await database.query(statement);
} catch (error) {
console.error(error);
}
}
// using the insertValues() function
async function updateDatabase(database) {
try {
// I am using 'await' here to get the resolved value.
// I'm not sure this is the direction you want to take.
const results = await Promise.all(
tableStatements.map(statement => insertValues(database, statement))
);
// do stuff with 'results'.. I'm just going to log them to the console
console.log(results);
} catch (error) {
console.error(error);
}
}
Here, insertValues() function doesn't return any value. Its operation on the database is entirely dependent on the query statement passed to it. I wrapped it within a try-catch block so as to catch any errors that might arise while performing the operation (s) above. More details on handling errors using try-catch can be found here.
Your promisified write to database looks ok, so we can update code from another part.
Let's rewrite it a little to use async/await and try/catch.
(async() => {
const promisifiedStatements = table_statements.map(i => insertValues(i));
try {
await Promise.all(promisifiedStatements);
readNodeData(session, nodes);
} catch(e){
console.log(e)
}
})();
I use here IIFE to use await behaviour.