TL;DR: In this article, you will learn how to use Retrofit, an HTTP client library, and Spring Boot to create a client for a RESTful API. You will use Spring Boot and its
RestController
functionality alongside Retrofit to develop a facade over GitHub's API. You will also learn how to convert JSON to POJOs (Plain Old Java Objects) using Google's Gson library and, last but not least, you will learn how to use the documentation of a RESTful API to your advantage. If you need, you can find the reference code developed throughout this article in this GitHub repository.“Learn how to use Retrofit, an HTTP client library, and Spring Boot to create a client for a RESTful API.”
Tweet This
Prerequisites
To follow this article along, you will need to have the following:
- JDK 8 or greater (JDK 10 was used in this article).
- A mature IDE (Integrated Development Environment) of your choice (i.e., IntelliJ, Netbeans, or Eclipse).
- An external HTTP client like
, Postman, or whatever.curl
- A GitHub account.
Once you have these, all you need is:
- A basic understanding of the client-server architecture.
- Some knowledge around the HTTP protocol.
- Some basic to intermediate knowledge of the Java programming language).
- Basic knowledge around Spring Boot.
What is a RESTful Client?
REST, which stands for Representational State Transfer, is an architectural style used to design web APIs (Application Programming Interfaces). REST is not a standard but rather a set of constraints used to design web APIs in well-designed format. In REST,
GET
requests should be used for resource lookup and PUT
, POST
, DELETE
for updating,
creating, and deleting a resource respectively.In this article, you will develop a Spring Boot app that is a RESTful client because it follows this constraints to communicate with a RESTful API (GitHub's API).
The explanation above is just a quick introduction, so you know what this is all about. As you can imagine, there is much more when it comes to the RESTful architecture. If you are curious, you can check the following resources after reading this article:
What Is Retrofit?
According to the official website, Retrofit is a type-safe HTTP client for Java and Android which was developed by Square. With Retrofit, all you need to do is declare a Java interface to represent your API. Then, you can pass the API configuration to Retrofit, and you will get back a Java class implementation of your interface.
Retrofit allows you to make both synchronous and asynchronous request to APIs. For simplicity, this article will focus on synchronous requests only. But you can check the other one right after reading the article.
One cool feature introduced by Retrofit is that it allows you to modify your API request through annotation on methods in your interface.
“What is cool about Retrofit is that it allows you to modify your API request through annotation on methods in your interface”
Tweet This
Why use the GitHub API?
To learn about Retrofit's capabilities, you could use many REST APIs out there. However, for a few reasons described below, you will use GitHub's REST API:
- Nearly all developers have GitHub accounts and, if you do not have one, creating one will only take a couple of seconds.
- GitHub API version 3 follows the REST architectural design and has a very good documentation.
- Last, but not least, GitHub allows you to query their API once you have an access token, username and password, or a secret key without requesting that you submit an application to use their API.
Generate token for use with GitHub API
First, as already mentioned, you will need a GitHub account. After creating (or signing into) your own account, click on your GitHub's profile photo (on the top right corner of the page) and choose the Settings option.
On that page, click on Developer settings on the left pane of the page, then click on Personal access tokens (again on the left pane) to arrive at the page where you manage access tokens. On this page, click on Generate new token. Now, GitHub will show you a form where it will ask you to enter a description for your token (you can insert something like "Retrofit Client") and will ask you to choose what Scopes you want to give to this token.
For that, you can select
repo
and delete_repo
scopes. Scopes determine the type of queries you will be able to do on GitHub's API with the token you will get.To finalize, click on Generate token to create the token. Store this new token somewhere safe on your laptop.
Creating a RESTful Client with Retrofit and Spring Boot
In this section, you will create a Spring Boot application that can
GET
, POST
, and DELETE
GitHub repositories via the GitHub API. For starters, go to the Spring Initializr page and fill out the form like this:- Generate a: At the top of the page, choose "Gradle Project".
- Group: You can leave this field as
.com.example
- Artifact: You can type
in this one.github-client
- Search for dependencies: Here, you will have to type "web" and choose the first option that appears. This will make a green label named "Web" appear on the Selected Dependencies section.
You can leave untouched the other options. After that, click on Generate Project. Clicking on this button will make this page send you the
github-client.zip
file with your project. Unzip this file in your preferred location (e.g., ~/Projects/
) and import it in your IDE.For the moment, your project contains only a single class called
GithubClientApplication
. Also, it contains two dependencies in the build.gradle
file: spring-boot-starter-web
and spring-boot-starter-test
. If you are using Java 10, you will have to update the build.gradle
file as follows// ... leave everything else untouched ... dependencies { // ... don't remove the other two dependencies ... compile('javax.xml.bind:jaxb-api:2.3.0') }
Make sure you allow your IDE to import the changes after updating this file.
Defining Domain Classes
According to the GitHub documentation, all requests to its API endpoint (i.e., to
https://api.github.com
) will receive JSON responses (at least, on the version 3 of the API). If you are going to receive JSON responses back from the GitHub API, you need Java classes to represent this data.As you don't want to waste time creating these classes, you can use the library that Eclipse (the IDE) uses on its Git plugin. This library contains all the classes that you will need in this article.
To import it, open the
build.gradle
file, and update it as follows:// ... leave the other configuration untouched ... dependencies { // ... leave the other dependencies untouched ... compile('org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5') }
To know more about this library, check out its page on GitHub.
Using Retrofit to Consume RESTful APIs
After installing the library that provides Java classes to represent responses from GitHub, you will create a service that communicates with GitHub's API. To do so, start by creating a class called
APIConfiguration
in your main package (e.g., com.example.githubclient
). This class will contain some references that you will need to make API requests to GitHub. Here it goes the contents of this file:package com.example.githubclient; public interface APIConfiguration { static final String API_BASE_URL = "https://api.github.com/"; static final String API_VERSION_SPEC = "application/vnd.github.v3+json"; static final String JSON_CONTENT_TYPE = "application/json"; }
As you can see, in this interface, you have a reference to the GitHub's API endpoint (
API_BASE_URL
), a reference to the current API version (API_VERSION_SPEC
), and a reference to the content type that you will use while communicating with the API (JSON_CONTENT_TYPE
).With that in place, you will turn GitHub's repository API into a Java interface. Start by importing Retrofit in your project. To do this, open the
build.gradle
file and update it as follows:// ... leave the other configuration untouched ... dependencies { // ... leave the other dependencies untouched ... implementation('com.squareup.retrofit2:retrofit:2.4.0') implementation('com.squareup.retrofit2:converter-gson:2.4.0') }
Then, create an interface called
RepositoryInterface
in the com.example.githubclient
package and replace the contents of this interface with this:package com.example.githubclient; import org.eclipse.egit.github.core.Repository; import org.eclipse.egit.github.core.event.DeletePayload; import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.POST; import retrofit2.http.Path; import java.util.List; public interface RepositoryInterface { @GET("user/repos") Call<List<Repository>> listRepos(@Header("Authorization") String accessToken, @Header("Accept") String apiVersionSpec); @DELETE("repos/{owner}/{repo}") Call<DeletePayload> deleteRepo(@Header("Authorization") String accessToken, @Header("Accept") String apiVersionSpec, @Path("repo") String repo, @Path("owner") String owner); @POST("user/repos") Call<Repository> createRepo(@Body Repository repo, @Header("Authorization") String accessToken, @Header("Accept") String apiVersionSpec, @Header("Content-Type") String contentType); }
As you will see, you don't need to provide an implementation for these methods. Retrofit will handle the methods in this interface for you.
The annotation at the top of every method (e.g.,
@POST
) defines the HTTP method that Retrofit will use when that method is called. The argument of the annotation (e.g., "user/repos"
) defines the path which will be added to the API endpoint when performing that particular request. The @Header
annotation is used to specify HTTP request headers that Retrofit will need while talking to GitHub, and @Body
is used to specify the HTTP request body that Retrofit will send to GitHub.Note that on top of the
deleteRepo
method, you defined path variables by using curly braces like {owner}
and {repo}
. Retrofit uses @Path
annotations to retrieve these variables so it can define the final URL that it will call.Now, you will need to create a service class that uses
RepositoryInterface
to query GitHub's API. So, start by creating a class called GitHubService
in the com.example.githubclient
package. In this file, insert the following code:package com.example.githubclient; import org.eclipse.egit.github.core.Repository; import org.eclipse.egit.github.core.event.DeletePayload; import org.springframework.stereotype.Service; import retrofit2.Call; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; import java.io.IOException; import java.util.List; @Service public class GitHubService implements APIConfiguration { private String accessToken; private RepositoryInterface service; public GitHubService() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); service = retrofit.create(RepositoryInterface.class); this.accessToken = "token " + System.getenv("ACCESS_TOKEN"); } public List<Repository> getRepositories() throws IOException { Call<List<Repository>> retrofitCall = service.listRepos(accessToken, API_VERSION_SPEC); Response<List<Repository>> response = retrofitCall.execute(); if (!response.isSuccessful()) { throw new IOException(response.errorBody() != null ? response.errorBody().string() : "Unknown error"); } return response.body(); } public Repository createRepository(Repository repo) throws IOException { Call<Repository> retrofitCall = service.createRepo(repo, accessToken, API_VERSION_SPEC, JSON_CONTENT_TYPE); Response<Repository> response = retrofitCall.execute(); if (!response.isSuccessful()) { throw new IOException(response.errorBody() != null ? response.errorBody().string() : "Unknown error"); } return response.body(); } public DeletePayload deleteRepository(String owner, String repoName) throws IOException { Call<DeletePayload> retrofitCall = service.deleteRepo(accessToken, API_VERSION_SPEC, repoName, owner); Response<DeletePayload> response = retrofitCall.execute(); if (!response.isSuccessful()) { throw new IOException(response.errorBody() != null ? response.errorBody().string() : "Unknown error"); } return response.body(); } }
The goal of this service is to provide a wrapper around
RepositoryInterface
to handle its interactions. That is, as you can see on the code above, you use Retrofit.Builder
to create an instance of RepositoryInterface
, then you define a variable called accessToken
with an access token retrieved from the OS environment variables (System.getenv("ACCESS_TOKEN")
), then you define three methods with similar implementations:
: This method wraps the call togetRepositories
passing to it yourRepositoryInterface::listRepos
, and theaccessToken
constant.API_VERSION_SPEC
: This method wraps the call tocreateRepository
passing to it aRepositoryInterface::createRepo
, yourrepo
, theaccessToken
constant, and theAPI_VERSION_SPEC
constant.JSON_CONTENT_TYPE
: This method wraps the call todeleteRepository
passing to it yourRepositoryInterface::deleteRepo
, theaccessToken
constant, aAPI_VERSION_SPEC
variable, and arepoName
variable.owner
If it is not clear yet how to use this service, don't worry, you will learn about it in the next section.
Expose the Retrofit Client as an Endpoint on Spring Boot
Finally, you have to expose your
GitHubService
through a Spring Boot RestController
. You will do so by creating a class in your application that will respond to web requests. As such, create a class called GitHubClientController
in the com.example.githubclient
package and insert the following code into it:package com.example.githubclient; import org.eclipse.egit.github.core.Repository; import org.eclipse.egit.github.core.event.DeletePayload; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.List; @RestController public class GitHubClientController { @Autowired private GitHubService githubService; @GetMapping("/repos") public List<Repository> getRepos() throws IOException { return githubService.getRepositories(); } @PostMapping("/repos") public Repository createRepo(@RequestBody Repository newRepo) throws IOException { return githubService.createRepository(newRepo); } @DeleteMapping("/repos/{owner}/{repo}") public DeletePayload deleteRepo( @PathVariable("owner") String owner, @PathVariable("repo") String repoName) throws IOException { return githubService.deleteRepository(owner, repoName); } }
Because of the
@Autowired
annotation on this class, Spring Boot will inject an instance of the GitHubService
class once you start your Spring Boot application. The getRepos
method that your new class exposes uses GitHubService::getRepositories
method to retrieve your GitHub repositories. The createRepo
method uses GitHubService::createRepository
to create a new GitHub repository. And, last but not least, deleteRepo
uses GitHubService::deleteRepository
method to delete a particular repository in your account.Testing Your Retrofit Application
With all these classes in place, you are now ready to test your application. To start it, you will need to execute the following commands on the terminal:
# set an OS env variable with your access token export ACCESS_TOKEN=<YOUR-ACCESS-TOKEN> # make Gradle run your Spring Boot app ./gradlew bootRun
Note: You will have to replace
with the personal access tokens you copied from GitHub previously. Your app, as you remember, calls<YOUR-ACCESS-TOKEN>
to get this variable.System.getenv("ACCESS_TOKEN")
Now, to list your repos, you can issue an HTTP
GET
request to http://localhost:8080/repos
:curl localhost:8080/repos
This will get you back an array with your GitHub repos. Then, to create a repo through your Retrofit client app, you can issue an HTTP
POST
request, as follows:curl -X POST -H 'Content-Type: application/json' -d '{ "name": "some-super-cool-repo", "description": "This is a test repository created using my GitHub client", "homepage": "https://github.com", "private": false, "has_issues": true, "has_projects": true, "has_wiki": true }' http://localhost:8080/repos
After that, if you visit
https://github.com/<YOUR-GITHUB-USER>/some-super-cool-repo
, replacing <YOUR-GITHUB-USER>
accordingly, you will see your brand new GitHub repo.Note: There are a lot of different options that you can change while creating your repo. In the example above, you used just a few options available. For more information, take a look at the attributes supported in the GitHub API documentation.
Finally, to delete your new repository, you can issue an HTTP
DELETE
request to /repos/<YOUR-GITHUB-USER>/some-super-cool-repo
(replacing <YOUR-GITHUB-USER>
accordingly):curl -X DELETE http://localhost:8080/repos/brunokrebs/some-super-cool-repo
Now, if you reload the page on GitHub, you will get an HTTP
404 Not Found
response.That's it! You just used Retrofit and Spring Boot to create a RESTful client easily. Cool, right?
“Using Retrofit to create API clients in Java is super easy.”
Tweet This
Aside: Securing Spring APIs with Auth0
Securing Spring Boot APIs with Auth0 is easy and brings a lot of great features to the table. With Auth0, we only have to write a few lines of code to get solid identity management solution, single sign-on, support for social identity providers (like Facebook, GitHub, Twitter, etc.), and support for enterprise identity providers (like Active Directory, LDAP, SAML, custom, etc.).
In the following sections, we are going to learn how to use Auth0 to secure APIs written with Spring Boot.
Creating the API
First, we need to create an API on our free Auth0 account. To do that, we have to go to the APIs section of the management dashboard and click on "Create API". On the dialog that appears, we can name our API as "Contacts API" (the name isn't really important) and identify it as
https://contacts.blog-samples.com
(we will use this value later).Registering the Auth0 Dependency
The second step is to import a dependency called
. This can be done on a Maven project by including the following configuration to auth0-spring-security-api
pom.xml
(it's not harder to do this on Gradle, Ivy, and so on):<project ...> <!-- everything else ... --> <dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.auth0</groupId> <artifactId>auth0-spring-security-api</artifactId> <version>1.0.0-rc.3</version> </dependency> </dependencies> </project>
Integrating Auth0 with Spring Security
The third step consists of extending the WebSecurityConfigurerAdapter class. In this extension, we use
JwtWebSecurityConfigurer
to integrate Auth0 and Spring Security:package com.auth0.samples.secure; import com.auth0.spring.security.api.JwtWebSecurityConfigurer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value(value = "${auth0.apiAudience}") private String apiAudience; @Value(value = "${auth0.issuer}") private String issuer; @Override protected void configure(HttpSecurity http) throws Exception { JwtWebSecurityConfigurer .forRS256(apiAudience, issuer) .configure(http) .cors().and().csrf().disable().authorizeRequests() .anyRequest().permitAll(); } }
As we don't want to hard code credentials in the code, we make
SecurityConfig
depend on two environment properties:
: This is the value that we set as the identifier of the API that we created at Auth0 (auth0.apiAudience
).https://contacts.blog-samples.com
: This is our domain at Auth0, including the HTTP protocol. For example:auth0.issuer
.https://blog-samples.auth0.com/
Let's set them in a properties file on our Spring application (e.g.
application.properties
):auth0.issuer:https://blog-samples.auth0.com/ auth0.apiAudience:https://contacts.blog-samples.com/
Securing Endpoints with Auth0
After integrating Auth0 and Spring Security, we can easily secure our endpoints with Spring Security annotations:
package com.auth0.samples.secure; import com.google.common.collect.Lists; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping(value = "/contacts/") public class ContactController { private static final List<Contact> contacts = Lists.newArrayList( Contact.builder().name("Bruno Krebs").phone("+5551987654321").build(), Contact.builder().name("John Doe").phone("+5551888884444").build() ); @GetMapping public List<Contact> getContacts() { return contacts; } @PostMapping public void addContact(@RequestBody Contact contact) { contacts.add(contact); } }
Now, to be able to interact with our endpoints, we will have to obtain an access token from Auth0. There are multiple ways to do this and the strategy that we will use depends on the type of the client application we are developing. For example, if we are developing a Single Page Application (SPA), we will use what is called the Implicit Grant. If we are developing a mobile application, we will use the Authorization Code Grant Flow with PKCE. There are other flows available at Auth0. However, for a simple test like this one, we can use our Auth0 dashboard to get one.
Therefore, we can head back to the APIs section in our Auth0 dashboard, click on the API we created before, and then click on the Test section of this API. There, we will find a button called Copy Token. Let's click on this button to copy an access token to our clipboard.
After copying this token, we can open a terminal and issue the following commands:
# create a variable with our token ACCESS_TOKEN=<OUR_ACCESS_TOKEN> # use this variable to fetch contacts curl -H 'Authorization: Bearer '$ACCESS_TOKEN http://localhost:8080/contacts/
Note: We will have to replace
with the token we copied from our dashboard.<OUR_ACCESS_TOKEN>
As we are now using our access token on the requests we are sending to our API, we will manage to get the list of contacts again.
That's how we secure our Node.js backend API. Easy, right?
Conclusion
In this article, you saw how Square's Retrofit library makes it easy for you to communicate with an API by creating a Java interface. However, what you have to keep in mind is that Retrofit can do much more than demonstrated here. For example, you can use Retrofit to build an Android client application for a backend API. Also, instead of performing synchronous requests, you can use Retrofit to perform asynchronous (non-blocking) request.
Retrofit is so cool, and there are so many other features in it. To learn about them, you can visit the documentation here.