Tech

API is inconsistent? Retrofit and Rx to the rescue!

Updated on
June 13, 2023
Table of content
Show
HIDE

The Intro

Hi, dear reader! I suspect, if you opened this link then you know how messy API responses can be. Wrong data types, an inconsistent structure of response etc. Especially when it comes to error responses, backend guys forget about style and same structure of responses all over the app. Maybe they had reasons for that, you never know. But my story today is not about blaming anyone, not today.

Anyway, us, front-end guys, should handle everything API returns and doesn’t return. I am going to show you my personal approach that might be useful for those who have troubles with error parsing (you might wanna use it not only for parsing errors and getting error descriptions).

The Problem

How was it? Once I realized that the project I was working on needed error parsing. Here are the examples of JSON response I was receiving:

1. {
"Message": "Error",
"Error_description": Null,
"Errors":[],
"Data":Null,
"Status": "Error"
}
2. {
"Message": Null,
"Data":Null,
"Errors": [
{
"1":"Error1",
"2":"Error2"
}
],
"Status": "Error",
"ExceptionType": Null
}
3. {
"Error_description": Null,
"Data":Null,
"Status": "No Error"
}

Now you saw them, all those dummy fields where such a coveted error is stored. And it wasn’t the last problem I got. Error strings were also coming with dividers in the description or split word by word. I didn’t want to make separate checks and parse each response to get that error description if any comes. So what did I do?

The Solution

Note: I am going to talk about Decorator pattern now, if you are not familiar with it, click here.

I decided to use Decorator design pattern. Why? In some cases, there is work you want to do after regular parsing and only for one or two responses. Why should I duplicate code or extend base parser class and change its behavior if I can build a new parser using previously created blocks of parsing logic I want to be in my newly created parser.
Let’s define simple ErrorResponseParser interface which describes parser behavior:

Interface ErrorResponseParser : Function, Single> {
Fun GetErrorString(): String
}

Since I am using Rx in this story and in the project, Function is an interface from io.reactivex.functions package, Single is a …. regular Single from io.reactivex.

Then let’s introduce ErrorResponseParserDecorator class:

ErrorResponseParserDecorator implements ErrorResponseParser and receives ErrorResponseParseras a constructor argument.

You might notice that in param is of typeResponse. It’s a default response class you can wrap your custom retrofit response with. It’s part of Retrofit library.

Now it’s time to create the first ErrorResponseParserimplementation:

CODE:https://gist.github.com/KravaDanil/64dc957ba9a994a80d9ded8932913aab.js

All the magic (not really) happens in the apply method. Let’s see what’s going on here. I check if it’s successful (I mean Response) and pass it to downstream using Single.just() operator. If it’s not, I am trying to fetch error description from response and based on code create proper type of exception with the message I got.

Note: Since Response’s error body is a stream, you can read it only once. Be sure not to call it somewhere else except BaseErrorResponseParser or handle it properly, otherwise, you’ll get an exception.

That’s the minimum requirement before we can start building, we got all interfaces and one base class we can use to start map process.

Let’s see how we can use our newly created BaseErrorResponseParser.

Let’s say we have an API endpoint:

@GET("GetCatsUrl")
Fun GetCats(): Single>
Note: CatsReponse is wrapped by retrofit Response class

And simply add proper map operator to parse response.

Fun GetCats(): Single = Api.GetCats()
.FlatMap(BaseErrorResponseParser())

Here you go! That’s it. Now your response is properly parsed.

More, more, more, gimme more!

Let’s get things a bit complicated!

Say we have to do something else based on parsed response earlier. Hmmm… For example, there is a certain error message “no cats found" we want to keep track of and once we encounter that message we’d like to transform error into another error, specific one.

Let’s create Decorator for this purpose:

Class CatsResponseParser(Parser: ErrorResponseParser) : ErrorResponseParserDecorator(Parser) {
Override Fun Apply(R: Response): Single {
Return Super.Apply(R)
}
}

It doesn’t have much so far. Let’s refresh it a bit and add some code.

Class CatsResponseParser(Parser: ErrorResponseParser) : ErrorResponseParserDecorator(Parser) {
Override Fun Apply(R: Response): Single {
Return Super.Apply(R)
.OnErrorResumeNext {
If(It.Message == "No Cats Found") {
Single.Error(CatsNotFoundException(It.Message))
} Else {
Single.Error(It)
}
}
}
}

Here we are! Now if our parser encounters “no cats found” error message it returns a new exception instead of existing one. This was just the simplest example of how you can use this approach.

Don’t forget to update API endpoint call

Override Fun GetCats(): Single =
Api.GetCats()
.FlatMap(CatsResponseParser(BaseErrorResponseParser()))

That was it! Thanks for reading. I hope you liked this one and learned something new.

Links and resources:

  1. Retrofit
  2. Decorator (tutorialspoint)
  3. Rx

Any questions and help appreciated 💛 💚
Stay tuned! A new article about Kotlin+ … + … + … is coming 😁