DEV Community

Yogesh Galav
Yogesh Galav

Posted on • Edited on

Stop using Try-Catch everywhere!

Yes, you read it write. Here I will only share my experience and if you agree or convinced with it give it a like so it could reach to more people.
I'm a Full Stack developer with 6 years of experience, and in this tenure I mostly worked with Rest Api's. I have seen my fellow developers write try catch in every controller or api code.
And also on frontend side in all api requests.
Now I will explain one by one why this is redundant code.

Why we need to stop using Try-catch block

1. It stops the error to be shown or debugged.
Let's suppose you have error coming in your browser console
Cannot read response of undefined
I hope most of you will have seen this error. When we debug, we will find out that it's coming from catch block of 'fetch' or 'axios' promise api call.

axios.get('/my-api')
.then((res)=>{
  //some error code
  let user_name = res.data.user.full_name
})
.catch((err)=>{
   if (500 === err.response.status) {
     //do this
   }
   if (422 === err.response.status) {
     //do this
   }
})
Enter fullscreen mode Exit fullscreen mode

Now last developer want to secure the 500 error by using try catch but guess what, it takes more effort for current developers to figure where's the error in then block, most of junior developers are not even able to recognize the above error.
Now you may argue all it takes is console statement in catch, but if that can't be pushed due to linting rules, that's extra effort as well, Isn't it?

Now move on to our next effort, sorry next step.
let's suppose the error is user property is not present in api, now we have to debug backend for that, Let's suppose our backend controller or logic code is written in this way

function myApiFunction(){
  try{
    const result = await models.Person.find({});
    res.locals.people = result;

    next();
  } catch (error){
    return next({
      log: `An error occurred at controller.myApiFunction ${error}`,
      status: 500,
      message: { err: 'An error occurred' }
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Now first thing is if you return or next from inside try block it will never reach to catch. Now actual problem here is we are returning variable with people not user.

Now Let's suppose the second scenario that there really was some error in our 'myApiFunction'. All we will get in frontend is
'An error occurred'. What did the frontend guy will understand from it. if you have made extra effort to not show error to user, you could have diverted that effort to make your code error free.

If you use Modern backend frameworks, you can define common error handler and you get structured error response anyway, so why did you put extra effort to write that try catch block.

2. It is repeating code, which violates DRY rule.
Now let's jump to second problem statement.
99% of time you will write same code in catch block in every controller and in every frontend api catch block.
The solution for this problem is using interceptors for package like axios in frontend and in backend, most modern frameworks provide elegant way to handle all your 500, 422, 401 etc errors at one place. You can even configure it for web and api request separately.

3. It is non Testable.
Most developers in my community or in my region don't write tests,
But the people who do realises how difficult it is complete code coverage.
For the developers who don't know what is code coverage it's representation of what part of your code is covered in test cases.
You can never cover catch block. Because if you know where error will occur you will fix it, you will not ship the error to client right?
The typescript and typed parameters in languages in php makes it further difficult for error to induce inside the function.

Where should we use try catch

Now I'm not saying we should never use try-catch, only want to save you effort and want to reduce your code. At some places it becomes necessary to use try-catch, Here we will discuss them

1. Function with multiple DB insertion and update queries.
Sometimes you have to run multiple queries for insertion and updation in your logic part, If any thing in between broke then some queries which already ran will be successful and will create garbage data. In this case we can use database transactions with try catch, here how it looks like

function myApiFunction(){
  await connection.beginTransaction();

  try {
    // multiple insertion and updation queries
    await connection.commit();
  } catch (error) {
    await connection.rollback();
  } finally {
    await connection.end();
  }

  return 'something';
}
Enter fullscreen mode Exit fullscreen mode

in above code we start declare db transaction, then executes all our queries then if all things successful we commit the results which finally changes the state of our database, else if any error occurs we can do rollback, and our database will return to original state before starting the transaction.

2. Third party Api call
Third party code is always a mystery, what error they return in which case we have to code it all, since we can't do all that in beginning we should must use try catch to catch all possible errors from third party api call.

try{
  stripe.doThis();
  twillio.doThat();
} catch(error) {
  log.info('Error occured in stripe while doing this.', error);
}
Enter fullscreen mode Exit fullscreen mode

3. Handling external resource like file Handling.
Similarly like third party services, we also don't know about external resources like files. In our application when we read document or media files we must take in scenario that this file may not be in format we assume,
For example you have csv import api, there's lot of uncertainty in this case.

  • The CSV may not be in correct format.
  • The file extension may not be correct.
  • The file may be corrupt or in other asci/utf code.
  • Query for importing or updating csv item may fail for some item. Hence we need to cover this all factors in separate try catch. Hope you enjoyed this post. Please support my post with like and if you have job reference please pass onto my linkedin profile.

4. Running heavy tasks in background or in loop
Heavy tasks like heavy file import, bulk email etc. can be done efficiently in background and off course there will be loop involved in it, So whole job shouldn't terminate if there's error in any single item or task, therefore we need to use try-catch for safe continuation of background job or loop.

bonus tip: Async-Await

If you use Promises in JS or Dart with async-await syntax then you must be using try-catch block with it and hence you might totally disagree with post, therefore I'm writting this bonus tip for improving your code quality. Consider the following code which is general case for calling api's in your code.

try {
  await firstApiCall()
} catch(error) { 
  handle(error) 
}
try {
  await secondApiCall()
} catch(error) { 
  handle(error) 
}
try {
  await thirdApiCall()
} catch(error) { 
  handle(error) 
}
Enter fullscreen mode Exit fullscreen mode

As you can see there's lot of repeating code or try-catch here, hence we will standardize our code to escape this hell. The only condition here is you have to use same response format on both frontend and backend.

async ApiCall(){
  const response = {data, success, message};
  try{
    response = await respectiveApiCall();
  } catch(error) {
    response = { data:null, success:false, message:error };
  }
  return response;
}
Enter fullscreen mode Exit fullscreen mode

This is my personal choice however if you don't like it or it doesn't fit you your case you can refer to this short video as well by Firship.io.
Step Image

Thanks and Regards,
Yogesh Galav

Top comments (0)