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
Strategy Pattern
Chain of Responsibility
Dependency Injection
... View more