Retrofit: API Integration Made Easy

Intro to Retrofit:

One of the most common tasks we have as developers is to make HTTP requests and interpret their responses. Often times we need to do this using Java and until now, there hasn’t been a concrete way of standardizing on an approach. That’s where Retrofit comes in.

Retrofit by Square provides an easy-to-use library for integrating APIs into Java applications. It provides the resources for transforming any API into a clean, simple, and type-safe interface. 

Sneak peek:

public interface VehicleAPIService {
         //code for calling the URL directly
         @GET(“/vehicles”)
         List<Vehicle> getAllVehicles();
}

Each method written with the Retrofit library represents a URL within a given API. The methods are annotated with request types such as:  @GET, @POST, @PUT, @DELETE, @HEAD. and come with optional capabilities of taking in query parameters, authentication headers, path parameters and several other nifty features we’ll discuss later in this tutorial. 

Topics of Focus:

  1.     How to implement an @GET request
  2.     How to use the @Query annotation
  3.     How to use the @Param annotation
  4.     How to implement an @POST request
  5.     Retrofit features for integrating with tricky APIs

To begin our discussion, we’ll pretend we’re developing an app for a car dealership. They want us to create an application for the handhelds carried by their salespeople. The handhelds should be able to search for vehicles, get a list of all vehicles, and have the option to add new vehicles as they come onto the lot. The dealership already has an API that makes all of these calls to the database, so all we need is an application that talks to the existing API.

Implementing the @GET Request:

The API we will be integrating with has a URL for retrieving all of the data for vehicles on the lot. 

Request URL:

http://www.mylottracker.com/vehicles

Response data:

[
    {
       “make“: “Jeep“,
       “model“: “Cherokee“,
       “color“: “SILVER“,
       “year“: 2013,
       “condition“: “BRAND_NEW
    },
    {
        “make“: “Dodge“,
        “model“: “Charger“,
        “color“: “WHITE“,
        “year“: 2011,
        “condition“: “DAMAGED
    },
    {
        “make“: “Chevrolet“,
        “model“: “Silverado“,
        “color“: “RED“,
        “year“: 2010,
        “condition“: “BRAND_NEW
    }
]

The lot appears to be pretty empty, but the dealership is new so we will have to remain patient.

The first thing we need to do when integrating the @GET request is create a Vehicle domain object that follows the same format as the JSON above. That will tell Retrofit how to parse the JSON under the hood so we can keep coding the fun stuff. 

public class Vehicle implements Serializable {

    private static final long serialVersionUID = 7766100460024055390L;
    private String make;
    private String model;
    private Color color;
    private int year;
    private Condition condition;

    // getters & setters…

}

Now, lets get started with Retrofit and create an interface and method with the @GET annotation to consume the response from the API call. The code should look like this.

public interface VehicleAPIService {

    @GET(“/vehicles”)
    List<Vehicle> getAllVehicles();

}

In this example, the @GET annotation describes type of request and the path defines the URL we are going to consume from. The return is what we want Retrofit to parse the JSON as, and the method name is completely up to us to define. Let’s put our interface to the test, shall we?

First we need to create a RestAdapter to define the base URL for our request. Then we need to create an instance of our new interface and call the @GET method for retrieving all of the vehicles. For testing purposes, we’ll implement the RestAdapter within the main method so we can quickly run the application and also add a few println’s to display the results.

public static void main(String… args) {
RestAdapter vehicleAdapter = new RestAdapter.Builder().setEndpoint(

       http://www.mylottracker.com“).build();

    //create an instance of the VehicleAPIService.
    VehicleAPIService vehicleService = vehicleAdapter
        .create(VehicleAPIService.class);

    //call the service to look up all vehicles.
    List<Vehicle> vehicles = vehicleService.getAllVehicles();

    //Display the results in console.
    System.out.println(“——– Vehicle List ——–“);
    System.out.println(); // for spacing consistency
    for(Vehicle vehicle : vehicles) {
       System.out.println(“make: ” + vehicle.getMake());
       System.out.println(“model: ” + vehicle.getModel());
       System.out.println(“year: ” + vehicle.getYear());
       System.out.println(“color: ” + vehicle.getColor());
       System.out.println(“condition: ” + vehicle.getCondition());
       System.out.println();
    }
    System.out.println(“——– End of List ———“);
}

Console Output:

——– Vehicle List ——–

make: Jeep
model: Cherokee
year: 2013
color: SILVER
condition: BRAND_NEW

make: Dodge
model: Charger
year: 2011
color: WHITE
condition: DAMAGED

make: Chevrolet
model: Silverado
year: 2010
color: RED
condition: BRAND_NEW

——– End of List ———

Worked like a charm!

Using the @Query annotation:

Most of the time, requests aren’t as simple as the one above, so let’s try adding a bit more complexity. Our API has a URL for retrieving vehicles of a certain condition. If we were to as it for a list of all of the brand new vehicles, the URL would look like this:

http://www.mylottracker.com/vehicles?condition=BRAND_NEW

In order to make this request using Retrofit, we need to use the @Query(parameterName) annotation. So, let’s add a new method to our VehicleAPIService and define the @Query variable with our “condition” string. The method should look like this:

@GET(“/vehicles”)
List<Vehicle> getVehiclesWithCondition(@Query(“condition”) String condition);

Let’s test the new @GET method by changing our original main method and include the new service call instead of the old one. The new main method should look like this. 

public static void main(String… args) {
RestAdapter vehicleAdapter = new RestAdapter.Builder().setEndpoint(

       http://www.mylottracker.com“).build();

    //create an instance of the VehicleAPIService.
    VehicleAPIService vehicleService = vehicleAdapter
        .create(VehicleAPIService.class);

    //OLD – call the service to look up all vehicles.
    //List<Vehicle> vehicles = vehicleService.getAllVehicles();

                    //NEW – call the service to look up vehicles that are BRAND_NEW.
                    List<Vehicle> vehicles = vehicleService.getVehiclesWithCondition(“BRAND_NEW”);

    //Display the results in console.
    System.out.println(“——– Vehicle List ——–“);
    System.out.println(); // for spacing consistency
    for(Vehicle vehicle : vehicles) {
       System.out.println(“make: ” + vehicle.getMake());
       System.out.println(“model: ” + vehicle.getModel());
       System.out.println(“year: ” + vehicle.getYear());
       System.out.println(“color: ” + vehicle.getColor());
       System.out.println(“condition: ” + vehicle.getCondition());
       System.out.println();
    }
    System.out.println(“——– End of List ———“);
}

Running the modified code gives us the new console output:

——– Vehicle List ——–

make: Jeep
model: Cherokee
year: 2013
color: SILVER
condition: BRAND_NEW

make: Chevrolet
model: Silverado
year: 2010
color: RED
condition: BRAND_NEW

——– End of List ———

Woop! Both vehicles show the “condition: BRAND_NEW.” Let’s keep moving.

Using the @Path annotation:

There are times when API requests call for path parameters to retrieve specific data. The car dealership’s API has a URL for passing in a VIN number for retrieving a specific vehicle’s data. It looks like this:

http://www.mylottracker.com/vehicles/{vin}

Retrofit provides an @Path annotation, which will work perfectly with the API’s URL format.

@GET(“/vehicles/{vin}“)
Vehicle contributors(@Path(“vin”) String vin);

We won’t go through the details of the response for this request because @Path is almost identical to the @Query and if you know how to test one you know how to test the other. Let’s switch gears and start sending our API data.

Implementing the @POST Request:

Our dealership’s API currently has a URL for adding new vehicles to the lot. The request takes in a JSON body, so to do this we need to add an @Body annotation to the method’s signature and pass a new vehicle into the POST.

Our new @POST method:

@POST(“/vehicles/new”)
void addVehicle(@Body Vehicle vehicle, Callback<Vehicle> cb);

Notice the Callback<Vehicle> parameter. This is required when making API requests that don’t return to help with error handling and potential retry logic. The Callback allows us to add extra processing when a request fails or succeeds. If you are a paranoid programmer like me, you could implement the success function to call the API again and ensure the vehicle was actually stored into the database. We won’t do that in this example, but it’s an idea for the future.

Calling the POST:

public static void main(String… args) {
RestAdapter vehicleAdapter = new RestAdapter.Builder().setEndpoint(

        “http://www.mylottracker.com”).build();

        //create an instance of the VehicleAPIService.
        VehicleAPIService vehicleService = vehicleAdapter
           .create(VehicleAPIService.class);

        //create a new vehicle
        Vehicle newVehicle = new Vehicle();
        newVehicle.setColor(Color.BLACK);
        newVehicle.setCondition(Condition.DAMAGED);
        newVehicle.setMake(“Oldsmobile”);
        newVehicle.setModel(“Alero”);
        newVehicle.setYear(1999);

        //call the service to add the new vehicle
        vehicleService.addVehicle(newVehicle, new Callback() {

           @Override
           public void failure(RetrofitError error) {
              //gets called when a network error occurs
              throw new RuntimeException(“Error! ” + error.getMessage());
           }

           @Override
           public void success(Vehicle arg0, Response arg1) {
              // no processing for this example – we’re good.
           }

    });
}

Now that we have a good understanding of Retrofit’s most common features, let’s dive into some of the cool stuff I’ve found useful when playing around with the library. 

Odds and Ends:

In the standardized world of Java, we might not be so lucky to have JSON parameters line up with our domain objects’ names. JSON parameters usually look more like this:

{
    “first_name”:”value”, // undesirable name
    “last_name”:”value”   // patterns… 🙁
}

With the way Retrofit works, our domain object would have to contain field names for “first_name” and “last_name.” Since those field names probably wouldn’t pass a peer review, Retrofit provides the ability to set converters on its RestAdapter. For our case, we just need to add a GsonConverter to the RestAdapter and let Retrofit take care of the conversion under the hood.

// create a converter to go from lowercase with underscores to camel case.
Gson gson = new GsonBuilder().setFieldNamingPolicy(

    FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();

// set the new converter on the RestAdapter
RestAdapter retrofit = new RestAdapter.Builder().setEndpoint(“http://www.mylottracker.com“)

    .setConverter(new GsonConverter(gson)).build();

This little converter tells Retrofit to rename all of the properties within the JSON to the camel-case format we desire within our domain objects. Now we can use the fields: “firstName” and “lastName” and continue on our way. 

Logging can also be very useful for diving into the HTTP requests and responses: 

RestAdapter vehicleAdapter = new RestAdapter.Builder().setEndpoint(“http://www.mylottracker.com”)
    .build();
vehicleAdapter.setLogLevel(LogLevel.FULL);

The @Headers annotation comes in handy when you need to set a content type or authentication headers.

@Headers({
    “Accept: application/json”
})
@GET(“/vehicles”)
List<Vehicle> getAllVehicles();

Conclusion:

Retrofit also has a ton of other features such as: response caching, custom error handlers, authentication features, and conversion capabilities that this article doesn’t cover, but I hope this gave you enough to get your feet wet!

One thought on “Retrofit: API Integration Made Easy

  1. Aaron J. Zirbes says:

    You can also have Retrofit return Observables if you are using JavaRx, GroovyRx or any of the http://reactivex.io/ libraries for the JVM.
    For more information, see the “RESPONSE OBJECT TYPE” section at http://square.github.io/retrofit/

  2. Steven Hicks says:

    This is a library that I’ve only but recently heard about. It looks exciting, and I”m a little surprised that it hasn’t gained a lot of traction yet.

  3. Abhishek Jaisingh says:

    just AWESOME!!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*