Introduction to EitherNet
EitherNet is a versatile, multi-platform tool designed to enhance the modeling of network API responses. This project provides a pluggable and sealed API result type that simplifies error handling when working with network requests. Though it is currently implemented for Retrofit on the JVM, its core concepts can be applied to various other platforms. The project is built using Kotlin and leverages Kotlin's sealed types to provide a type-safe mechanism for API response handling.
What is EitherNet?
When working with network APIs, handling responses and errors is a crucial aspect of application development. Typically, Retrofit, a popular networking library for Android, uses exceptions to manage errors. EitherNet improves upon this by providing a clearer, more manageable way to handle both successful and erroneous API responses. The central concept in EitherNet is the ApiResult<out T, out E>
, where T
denotes a successful response type and E
signifies a possible error type.
Key Features
Seamless Error Handling with Sealed Types
EitherNet organizes API results into sealed subtypes under ApiResult
: Success
for successful responses and Failure
for errors. Failure itself can be further broken down into NetworkFailure
, ApiFailure
, HttpFailure
, and UnknownFailure
, allowing developers to handle a wide range of error scenarios using structured Kotlin when
expressions.
Here's an example of how it might look in code:
when (val result = myApi.someEndpoint()) {
is Success -> doSomethingWith(result.response)
is Failure -> when (result) {
is NetworkFailure -> showError(result.error)
is HttpFailure -> showError(result.code)
is ApiFailure -> showError(result.error)
is UnknownFailure -> showError(result.error)
}
}
Retrofit Integration
To integrate EitherNet with Retrofit, developers simply need to adjust their endpoint definitions to return ApiResult
types and utilize the provided converter and adapter factories:
interface TestApi {
@GET("/")
suspend fun getData(): ApiResult<SuccessResponse, ErrorResponse>
}
val api = Retrofit.Builder()
.addConverterFactory(ApiResultConverterFactory)
.addCallAdapterFactory(ApiResultCallAdapterFactory)
.build()
.create<TestApi>()
Flexible Decoding and Pluggability
For scenarios where HTTP error responses need special decoding, EitherNet provides the @DecodeErrorBody
annotation, enabling detailed error type parsing based on HTTP status codes. This feature, alongside customizable Converter
factories, offers developers fine-grained control over how responses are processed.
Retry Mechanism
EitherNet includes a built-in, configurable method for retrying API calls using exponential backoff. This is useful for handling transient network errors by retrying requests multiple times with increasing delays between attempts.
val result = retryWithExponentialBackoff(
maxAttempts = 3,
initialDelay = 500.milliseconds,
delayFactor = 2.0,
maxDelay = 10.seconds,
jitterFactor = 0.25,
onFailure = null
) {
api.getData()
}
Testing Support
EitherNet comes with a Test Fixtures component that provides tools such as the EitherNetController
for efficient and easy testing. This setup is similar to the OkHttp MockWebServer
and allows developers to predefine API responses to test various scenarios within their applications.
Installation
Developers can include EitherNet in their project by adding the following dependencies to their Gradle build configuration:
dependencies {
implementation("com.slack.eithernet:eithernet:<version>")
implementation("com.slack.eithernet:eithernet-integration-retrofit:<version>")
// Test fixtures
testImplementation(testFixtures("com.slack.eithernet:eithernet:<version>"))
}
For the most up-to-date versions and snapshot builds, developers can check Maven Central or Sonatype's repository.
Conclusion
EitherNet is a powerful and flexible tool for developers seeking a more organized and manageable way to handle network API responses in their Kotlin applications. By leveraging sealed types and a structured approach to error handling, EitherNet simplifies the traditionally complex task of interfacing with network APIs, thereby increasing code readability and reliability.