If you’re working with Spring, check out "REST With Spring":

>> CHECK OUT THE COURSE

1. Overview

This article focuses on how to implement a simple RxJava-ready REST Client using Retrofit.

We’ll build an example application interacting with the GitHub API – using the standard Retrofit approach, and then we’ll enhance it using RxJava to leverage the advantages of Reactive Programming.

2. Plain Retrofit

Let’s first build an example with Retrofit. We’ll use the GitHub APIs to get a sorted list of all the contributors that have more than 100 contributions in any repository.

2.1. Maven Dependencies

To start a project with Retrofit, let’s include these Maven artifacts:

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>retrofit</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>converter-gson</artifactId>
    <version>2.3.0</version>
</dependency>

For the latest versions, have a look at retrofit and converter-gson on Maven Central repository.

2.2. API Interface

Let’s create a simple interface:

public interface GitHubBasicApi {

    @GET("users/{user}/repos")
    Call<List> listRepos(@Path("user") String user);
    
    @GET("repos/{user}/{repo}/contributors")
    Call<List> listRepoContributors(
      @Path("user") String user,
      @Path("repo") String repo);   
}

The listRepos() method retrieves a list of repositories for a given user passed as a path parameter.

The listRepoContributers() method retrieves a list of contributors for a given user and repository, both passed as path parameters.

2.3. Logic

Let’s implement the required logic using Retrofit Call objects and normal Java code:

class GitHubBasicService {

    private GitHubBasicApi gitHubApi;

    GitHubBasicService() {
        Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("https://api.github.com/")
          .addConverterFactory(GsonConverterFactory.create())
          .build();

        gitHubApi = retrofit.create(GitHubBasicApi.class);
    }

    List<String> getTopContributors(String userName) throws IOException {
        List<Repository> repos = gitHubApi
          .listRepos(userName)
          .execute()
          .body();

        repos = repos != null ? repos : Collections.emptyList();

        return repos.stream()
          .flatMap(repo -> getContributors(userName, repo))
          .sorted((a, b) -> b.getContributions() - a.getContributions())
          .map(Contributor::getName)
          .distinct()
          .sorted()
          .collect(Collectors.toList());
    }

    private Stream<Contributor> getContributors(String userName, Repository repo) {
        List<Contributor> contributors = null;
        try {
            contributors = gitHubApi
              .listRepoContributors(userName, repo.getName())
              .execute()
              .body();
        } catch (IOException e) {
            e.printStackTrace();
        }

        contributors = contributors != null ? contributors : Collections.emptyList();

        return contributors.stream()
          .filter(c -> c.getContributions() > 100);
    }
}

3. Integrating with RxJava

Retrofit lets us receive calls results with custom handlers instead of the normal Call object by using Retrofit Call adapters. This makes it possible to use RxJava Observables and Flowables here.

3.1. Maven Dependencies

To use RxJava adapter, we need to include this Maven artifact:

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>adapter-rxjava</artifactId>
    <version>2.3.0</version>
</dependency>

For the latest version please check adapter-rxjava in Maven central repository.

3.2. Register RxJava Call Adapter

Let’s add RxJavaCallAdapter to the builder:

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("https://api.github.com/")
  .addConverterFactory(GsonConverterFactory.create())
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  .build();

3.3. API Interface

At this point, we can change the return type of interface methods to use Observable<…> rather than Call<…>. We may use other Rx types like Observable, Flowable, Single, Maybe, Completable.

Let’s modify our API interface to use Observable:

public interface GitHubRxApi {

    @GET("users/{user}/repos")
    Observable<List<Repository>> listRepos(@Path("user") String user);
    
    @GET("repos/{user}/{repo}/contributors")
    Observable<List<Contributer>> listRepoContributors(
      @Path("user") String user,
      @Path("repo") String repo);   
}

3.4. Logic

Let’s implement it using RxJava:

class GitHubRxService {

    private GitHubRxApi gitHubApi;

    GitHubRxService() {
        Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("https://api.github.com/")
          .addConverterFactory(GsonConverterFactory.create())
          .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
          .build();

        gitHubApi = retrofit.create(GitHubRxApi.class);
    }

    Observable<String> getTopContributors(String userName) {
        return gitHubApi.listRepos(userName)
          .flatMapIterable(x -> x)
          .flatMap(repo -> gitHubApi.listRepoContributors(userName, repo.getName()))
          .flatMapIterable(x -> x)
          .filter(c -> c.getContributions() > 100)
          .sorted((a, b) -> b.getContributions() - a.getContributions())
          .map(Contributor::getName)
          .distinct();
    }
}

4. Conclusion

Comparing the code before and after using RxJava, we’ve found that it has been improved in the following ways:

  • Reactive – as our data now flows in streams, it enables us to do asynchronous stream processing with non-blocking back pressure
  • Clear – due to its declarative nature
  • Concise – the whole operation can be represented as one operation chain

All the code in this article is available over on GitHub.

The package com.baeldung.retrofit.basic contains the basic retrofit example while the package com.baeldung.retrofit.rx contains the retrofit example with RxJava integration.

The new Certification Class of "REST With Spring" is finally out:

>> CHECK OUT THE COURSE