Wrap a REST API with Retrofit

Building a type-safe wrapper around a REST API is a pain. You need to deal with separate HTTP calls, dynamic construction of query strings, casting to your data types, handling exceptions etc. Luckily the smart folks at Square released a tool called Retrofit to help you cope with those problems. Retrofit asks you to provide an interface to the REST API in the form of a Java interface along with some annotations. It takes care of all the HTTP calls, query string construction, and exception handling, and leaves you with some basic POJO creation. We will take a look at how to wrap the St. Louis Fed’s REST API to retrieve macro economic data. The first thing you do is create POJOS for each of the objects you would like to pass to your application.

Source code: Freddie

Create a simple POJO you want to populate with data

public class Series
{
    private String id;
    private String title;
    private List<Observation> data;
    private String seasonalAdjustment;
    private String seasonalAdjustmentShort;
    private String frequency;
    private String frequencyShort;
    private String units;
    private String unitsShort;
    private Date realtimeStart;
    private Date realtimeEnd;
    private Date observationStart;
    private Date observationEnd;
    private Date lastUpdated;
    private Integer popularity;
    private String notes;
}

Create an interface that describes the API endpoint

The interface will create the query string for you. Every method description you add here becomes one possible API endpoint call. GET, POST, PUT, DELETE, and HEAD are available. The @Query annotations allow you to pass dynamic data to these endpoints. (more info)

import retrofit.http.GET;
import retrofit.http.Query;

public interface IFredApiService
{

    @GET("/series")
    Series getSeries(
            @Query("series_id") String seriesName,
            @Query("api_key") String apiKey,
            @Query("file_type") String dataReturnType);
}

Create a custom GSON deserializer

Retrofit has chosen Google’s GSON library as it’s primary serialization/deserialization library. Here is the only interesting snippet from the Series Deserializer:

@Override
    public Series deserialize(
        JsonElement json, 
        Type arg1, 
        JsonDeserializationContext arg2) 
    throws JsonParseException
    {
        Series series = new Series();
        JsonObject obj = (JsonObject) json;
        if (obj.has("seriess"))
        {
            JsonArray seriesCollection = 
                (JsonArray) obj.get("seriess");
            if (seriesCollection.size() == 1)
            {
                JsonObject seriesObj = 
                    (JsonObject) seriesCollection.get(0);
                series.setId(
                    seriesObj.get("id").getAsString());
                series.setTitle(
                    seriesObj.get("title").getAsString());
                series.setFrequency(
                    seriesObj.get("frequency").getAsString());
.... more getting and setting ....
            }
        }
    }

Now tie everything together in a client

Full client code

Here is what we need:

Our Series Deserializer:

Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setDateFormat("yyyy-MM-dd")
.registerTypeAdapter(Series.class, new SeriesDeserializer())
.create();

Hook your GSON deserializer up to Retrofit’s magic RestAdapter:

RestAdapter adapter = new RestAdapter.Builder()
                    .setEndpoint(endPoint)
                    .setLogLevel(LogLevel.NONE)
                    .setConverter(new GsonConverter(gson))
                    .build();

The RestAdapter instantiates an object with all the methods defined in your API interface when you pass it the class name of your interface like so:

IFredApiService service = restAdapter.create(IFredApiService.class);

And that’s really it

Now you can do the following:

public Series getSeriesById(String seriesId) throws Exception
{
    Series series = null;
    try {
        series = service.getSeries(seriesId, apiKey, "json");
    } catch (Exception e) {
        throw e;
    }
    return series;
}

Retrofit even allows for the use of Observable<?> return types which are provided by Netflix’s RxJava library. This approach makes the user of your service agnostic as to whether he calls you synchronously or asynchronously. You as the provider of the service get to decide and the consumer does not have to change the way he calls your service when you decide to execute your processes asynchronously. Read more about RxJava

Happy wrapping!

Let me know what you think => @bweidlich

 
71
Kudos
 
71
Kudos

Now read this

Using Hazelcast and RxJava to build agnostic clients

I am currently refactoring a suite of services that are all Hazelcast cache instances and part of a cache cluster. One of my first steps was to look at client-service communication endpoints and see whether I can’t decouple clients and... Continue →