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.