Creating REST Clients for JAX-RS based Webservices with Netflix Feign

October 22nd, 2015 by

For us developers there plenty of libraries exist helping us in deriving and generating clients for existing RESTful web-services and I have already covered some of the in this blog (e.g. the JAX-RS Client API). Nevertheless, I’d like to share my experience with another interesting lightweight library here: Netflix Feign.

Feign offers a nice fluent-builder API, a rich integration for  common libraries and APIs like JAX-RS, Jackson, GSON, SAX, JAX-B, OkHttp, Ribbon, Hystrix, SLF4J and more and last bot not least, it is setup easy and the service contracts are specified using interfaces and annotations.

In the following tutorial I’d like to demonstrate how to derive a service binding from a simple, JAX-RS based RESTful web-service, how to create the service adapter using the integrated builder and at last, performing CRUD operations.

JAX-RS Service Definition in Eclipse IDE

JAX-RS Service Definition in Eclipse IDE

 

Dependencies

We’re adding the following dependencies to our pom.xml (using Maven):
  • feign-jaxrs: Feign core functionality and the ability to understand the JAX-RS annotations.
  • feign-jackson: Allows us to use the Jackson JSON Processor for serialization and deserialization – I like this library.
<dependencies>
	<dependency>
		<groupId>com.netflix.feign</groupId>
		<artifactId>feign-jaxrs</artifactId>
		<version>8.11.0</version>
	</dependency>
	<dependency>
		<groupId>com.netflix.feign</groupId>
		<artifactId>feign-jackson</artifactId>
		<version>8.11.0</version>
	</dependency>
</dependencies>

RESTful Web-service

For our JAX-RS based web-service we’re simply using a pre-made one from another tutorial of mine: “JAX-RS 2.0 REST Client Features by Example” (sources or war-file).

General knowledge about the JAX-RS annotations used here is assumed but to summarize the features of this concrete web-service – it exposes the following basic CRUD operations:

  • .. list existing books (returned in JSON format)
  • .. create a new book (returns created book in JSON format)
  • .. find a book by its identifier (returns book in JSON format)
  • .. delete a book
@Path("/book")
public class BookStoreService {
	private BookRepository bookRepository;
 
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response saveBook(final Book book) {
		Book bookPersisted = bookRepository.saveBook(book);
		return Response.ok(bookPersisted).build();
	}
 
	@DELETE
	@Path("/{id}")
	public Response deleteBook(final @PathParam("id") String id) {
		bookRepository.deleteBook(id);
		return Response.ok().build();
	}
 
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Response getAll() {
		List<Book> books = bookRepository.getAll();
		GenericEntity<List<Book>> bookWrapper = new GenericEntity<List<Book>>(
				books) {
		};
		return Response.ok(bookWrapper).build();
	}
 
	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getById(final @PathParam("id") String id) {
		Book book = bookRepository.getById(id);
		return Response.ok(book).build();
	}
}

Writing the Client

Now that we’ve got a target web-service, we may start with our client implementation..

Book Entity

First of all, we’re re-using the book entity from the other tutorial mentioned above so that this is our simple book entity:

package com.hascode.tutorial;
 
import java.math.BigDecimal;
import java.util.Calendar;
 
public class Book {
	private String id;
	private String title;
	private BigDecimal price;
	private Calendar published;
 
	// getter, setter, toString ommitted ..
}

A book has an identifier, a title, a price and a publication date.

Service Definition

Now the fun part: We’re simply copying our service definition into a new interface.

As often in a project the specification of a web-service is already specified using an interface and separated from its concrete implementation in a class there is even less to do using this approach.

package com.hascode.tutorial;
 
import java.util.List;
 
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
 
@Path("/book")
public interface BookService {
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Book saveBook(final Book book);
 
	@DELETE
	@Path("/{id}")
	public void deleteBook(final @PathParam("id") String id);
 
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<Book> getAll();
 
	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Book getById(final @PathParam("id") String id);
}

Client Example

To create our client, Feign offers us a nice fluent builder API called via Feign.builder()

In the following example, we’re binding our service interface, we’re adding the Jackson library as encoder and decoder (.encoder(), .decoder()) and we’re adding the JAXRSContract as contract (.contract()) so that Feign is able to recognize and handle JAX-RS specific information like method-definitions, path-parameters, content-type-definitions and so on…

Having created a binding to our web-service, we’re now calling each functionality of the REST service as we ..

  • list existing books .. we haven’t created one yet, so nothing is returned
  • create a new book
  • list existing books again, one is returned
  • find the book by its identifier
  • remove the book
  • list existing books again to verify that nothing is returned
package com.hascode.tutorial;
 
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.List;
 
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.jaxrs.JAXRSContract;
 
public class BookServiceClient {
 
	public static void main(String[] args) {
		BookService bookService = Feign.builder().contract(new JAXRSContract()).encoder(new JacksonEncoder())
				.decoder(new JacksonDecoder()).target(BookService.class, "http://localhost:8080/tutorial/rs");
 
		System.out.println("fetching existing books..");
		List<Book> books = bookService.getAll();
		System.out.println(String.format("%s books received", books.size()));
		books.forEach(System.out::println);
 
		Book book = new Book(null, "One Book", BigDecimal.TEN, Calendar.getInstance());
		System.out.println("saving new book: " + book);
		bookService.saveBook(book);
 
		System.out.println("fetching existing books..");
		books = bookService.getAll();
		System.out.println(String.format("%s books received", books.size()));
		books.forEach(System.out::println);
 
		String id = books.get(0).getId();
		System.out.println("fetching book by id: " + id);
		Book byId = bookService.getById(id);
		System.out.println(String.format("book for id %s found: %s", id, byId));
 
		System.out.println("removing book with id: " + id);
		bookService.deleteBook(id);
 
		System.out.println("fetching existing books..");
		books = bookService.getAll();
		System.out.println(String.format("%s books received", books.size()));
		books.forEach(System.out::println);
	}
 
}

Running the Client

Assuming that our server is running and responding (see this tutorial of mine for simply download and startup of the sample server), we’re now ready to run our client example from above using our IDE or in the console by using Maven like this.

$ mvn exec:java -Dexec.mainClass=com.hascode.tutorial.BookServiceClient
[..]
fetching existing books..
0 books received
saving new book: Book [id=null, title=One Book, price=10, published=2015-10-20T12:26:21.741Z]
fetching existing books..
1 books received
Book [id=3633abdc-39c8-4fcc-a61b-80827983de6b, title=One Book, price=10, published=2015-10-20T12:26:21.741Z]
fetching book by id: 3633abdc-39c8-4fcc-a61b-80827983de6b
book for id 3633abdc-39c8-4fcc-a61b-80827983de6b found: Book [id=3633abdc-39c8-4fcc-a61b-80827983de6b, title=One Book, price=10, published=2015-10-20T12:26:21.741Z]
removing book with id: 3633abdc-39c8-4fcc-a61b-80827983de6b
fetching existing books..
0 books received
[..]

Feign Additional Features

In this tutorial I have covered merely one tiny aspect of this library, please feel free to take a look at the Feign project as there are many other exciting features as..

  • Handling of multiple API interfaces
  • Integrations for popular libraries and APIs like GSON, SAX, JAX-B, OkHttp, Ribbon, Hystrix, SLF4J
  • Specification of base APIs using inheritance
  • Special logging support
  • Request interceptors
  • Parameter conversion API

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/feign-tutorial.git

Resources

Other REST Tutorials of mine

Please feel free to have a look at these tutorials of mine covering different aspects of handling or creating REST clients or services.

Netflix Hystrix

If interested in using Netflix Hystrix, please feel free to read the following tutorial of mine: “Resilient Architecture in Practice – Circuit Breakers for Java: Failsafe, Javaslang, Hystrix and Vert.x“.

Article Updates

  • 2017-02-14: Link to circuit-breaker article added.

    Tags: , , , , , , , , , , , , , , ,

    One Response to “Creating REST Clients for JAX-RS based Webservices with Netflix Feign”

    1. harsh Says:

      Thanks for sharing it. I have been thinking too to use netflix feign at work project.

    Search
    Categories