Happy Business Starts Here

Re: API Error Handling Best Practice

Highlighted
Zuora Alumni

API Error Handling Best Practice

What are some Error Handling best practices and guidelines?

 



If you found my answer helpful, please give me a kudo ↑
Help others find answers faster by accepting my post as a solution √

1 ACCEPTED SOLUTION

Accepted Solutions
Highlighted
Zuora Alumni

Re: API Error Handling Best Practice

 

Error Handling is a fundamental part of designing a robust API Client

 

At a minimum, error handling should have the following qualities

 

  • error detection - ability to catch/detect an error was returned
  • retry logic - should the operation be retried and if so, how many times and with what frequency
  • client side error logging/reporting/exception handling - raising and logging the error

 

 

 

While this basic approach is certainly better than no error handling, it's taking a static approach for all types of errors.

 

If we consider that different errors occur for different reasons, handling them all the same way may not be the optimal approach.

 

Having some type of modular error handling that can be tailored to specific scenarios would be ideal, and ideal for the Strategy Pattern.

 

This approach adds a layer of complexity to your error handling code, but allows you to implement flexible error handling based on the error received and at the same time, not end up with a mountain of "if-then-else" statements.

 

A different appraoch to API Client error handling might look something like this

 

  • error detection
  • error handler selection
  • error handling

 

Error Handler Selection

 

The "how" the selection is done is really out of the scope of this discussion (which is more abstract)

 

The key point is selecting different error handlers based on the detected error.

 

The error handler in this context should not contain any business specific logic, but implement the strategy for retrying, max retries, etc.

 

Examples:

 

  • Syntax error - something is wrong with the query / call
    • Strategy : No Retry
      • don't retry the call, as it will just fail again anyway
      • possibly cache/mark the call to not execute again - as its going to fail
      • log/raise the issue appropriately so it can be identified, analyzed, and fixed
  • Client Side Timeouts - connection is not established within N-number of seconds
    • Strategy : Simple Retry
      • Immediately retry the call N-number of times - try to aggresively establish the connection
      • fail after N-number of attempts
      • log/raise the error
  • LOCK_COMPETITION - target resource is exclusively locked
    • Strategy : Sliding Retry
      • static wait
      • retry the call N-number of times - try to aggresively complete the call
      • after N-number of times, use a sliding wait
      • retry the call N-number of times - backing off with an increasing time value
      • fail after N-number of attemps
      • log/rais the error

 

Retrying the Call

 

There are a few issues that are going to have be addressed, irregardless of implementation.

 

  • which code is executing the callout?
  • how do we handle multiple errors, e.g. - on retry the errors are different?
  • how do we avoid getting into a endless loop of error handling?

 

One simple way to deal with this is via Dependency Injection.

 

Designing the code that does the callout to be "injected" into a generic callout executor allows you to decouple the retry logic and the callout logic.

 

The generic callout executor can then handle actually making the call, and call the correct handler.

 

It is also an easy context to handle edge cases like endless loops, etc.

 

 

Error Handling

 

In this context, error handling does not contain business logic, that should be higher up in the stack.

 

At this context, the Error handler should only be concerned with the correct Strategy for retrying and determining when to finally fail the call and raising the error.  At that point, the raised error should then be handled by a business specific error handler.

 

Although encapsulating all the logic in a single error handler is fine, implementing a chain of handlers would be more extensible and resusable, but this is really the topic of another discussion.

 

One example of this is verbose logging.  Verbose logging is expensive, it takes time and space - so naturally not something to be done on every single call.

 

A chain is flexible and configurable at runtime, and so additional handling could be executed after an error is hit N-number of times. etc., or alternatively could be turned on without having to restart the execution container in debug mode.

 

Using a chain of handlers, one type of handler could implement additional low level logging of things like

 

  • verbose HTTP request / response including headers
  • OS level info - tcpdump, traceroute, dig, etc. 

 

Additional Reference

 



If you found my answer helpful, please give me a kudo ↑
Help others find answers faster by accepting my post as a solution √

View solution in original post

1 REPLY 1
Highlighted
Zuora Alumni

Re: API Error Handling Best Practice

 

Error Handling is a fundamental part of designing a robust API Client

 

At a minimum, error handling should have the following qualities

 

  • error detection - ability to catch/detect an error was returned
  • retry logic - should the operation be retried and if so, how many times and with what frequency
  • client side error logging/reporting/exception handling - raising and logging the error

 

 

 

While this basic approach is certainly better than no error handling, it's taking a static approach for all types of errors.

 

If we consider that different errors occur for different reasons, handling them all the same way may not be the optimal approach.

 

Having some type of modular error handling that can be tailored to specific scenarios would be ideal, and ideal for the Strategy Pattern.

 

This approach adds a layer of complexity to your error handling code, but allows you to implement flexible error handling based on the error received and at the same time, not end up with a mountain of "if-then-else" statements.

 

A different appraoch to API Client error handling might look something like this

 

  • error detection
  • error handler selection
  • error handling

 

Error Handler Selection

 

The "how" the selection is done is really out of the scope of this discussion (which is more abstract)

 

The key point is selecting different error handlers based on the detected error.

 

The error handler in this context should not contain any business specific logic, but implement the strategy for retrying, max retries, etc.

 

Examples:

 

  • Syntax error - something is wrong with the query / call
    • Strategy : No Retry
      • don't retry the call, as it will just fail again anyway
      • possibly cache/mark the call to not execute again - as its going to fail
      • log/raise the issue appropriately so it can be identified, analyzed, and fixed
  • Client Side Timeouts - connection is not established within N-number of seconds
    • Strategy : Simple Retry
      • Immediately retry the call N-number of times - try to aggresively establish the connection
      • fail after N-number of attempts
      • log/raise the error
  • LOCK_COMPETITION - target resource is exclusively locked
    • Strategy : Sliding Retry
      • static wait
      • retry the call N-number of times - try to aggresively complete the call
      • after N-number of times, use a sliding wait
      • retry the call N-number of times - backing off with an increasing time value
      • fail after N-number of attemps
      • log/rais the error

 

Retrying the Call

 

There are a few issues that are going to have be addressed, irregardless of implementation.

 

  • which code is executing the callout?
  • how do we handle multiple errors, e.g. - on retry the errors are different?
  • how do we avoid getting into a endless loop of error handling?

 

One simple way to deal with this is via Dependency Injection.

 

Designing the code that does the callout to be "injected" into a generic callout executor allows you to decouple the retry logic and the callout logic.

 

The generic callout executor can then handle actually making the call, and call the correct handler.

 

It is also an easy context to handle edge cases like endless loops, etc.

 

 

Error Handling

 

In this context, error handling does not contain business logic, that should be higher up in the stack.

 

At this context, the Error handler should only be concerned with the correct Strategy for retrying and determining when to finally fail the call and raising the error.  At that point, the raised error should then be handled by a business specific error handler.

 

Although encapsulating all the logic in a single error handler is fine, implementing a chain of handlers would be more extensible and resusable, but this is really the topic of another discussion.

 

One example of this is verbose logging.  Verbose logging is expensive, it takes time and space - so naturally not something to be done on every single call.

 

A chain is flexible and configurable at runtime, and so additional handling could be executed after an error is hit N-number of times. etc., or alternatively could be turned on without having to restart the execution container in debug mode.

 

Using a chain of handlers, one type of handler could implement additional low level logging of things like

 

  • verbose HTTP request / response including headers
  • OS level info - tcpdump, traceroute, dig, etc. 

 

Additional Reference

 



If you found my answer helpful, please give me a kudo ↑
Help others find answers faster by accepting my post as a solution √

View solution in original post