Using Feign to consume external REST API in Spring boot

Some time we need to consume external services in order to serve content to our users. In our quotes service also we can service users images for various topics as well. In order to do so i have used Pexels for fetching images. Pexels provide royalty free images. In order to consume Pexels API you need to obtain a developer API key. Please sign up for developer key from https://www.pexels.com/api

I suggest to go through the documentation of pexels API.

There are multiple way to consume external REST Api in Spring boot application. Netflix’s feign client is one of them and is quite easy to use. Spring boot provides integration with feign-client using spring cloud package. In order to use feign client update your pom.xml with below dependencies.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>

Add property spring-cloud.version.

<spring-cloud.version>Finchley.SR1</spring-cloud.version>

Let’s create a contract for ImageService. And Image Pojo class

public class Image {
private final String source;
private final String photographer;
private final String photographerUrl;
private final String imageUrl;

  public Image(String source, String photographer, String imageUrl, String photographerUrl) {
this.source = source;
this.photographer = photographer;
this.imageUrl = imageUrl;
this.photographerUrl = photographerUrl;
}
//Getters and Setters
}

public interface ImageService {
//Returns random images pagewise
public List<Image> getImages(int page);

// Returns list of images for a topic
public List<Image> getImages(String topic, int page);
}

We have defined contract first because this function’s implementation may vary depending on sources. I have implemented this for Pexels you may use other (eg: unsplash)

Pexel provides response in its own format and we will have to re-transform response received pexel into our Image pojo. As these transformation is specific to pexel, we will create a concrete implementation of ImageService and will put all pexel related codes into it.

@Service
public class PexelsImageService implements ImageService {
@Override
  public List<Image> getImages(int page) {
return null;
}
@Override
  public List<Image> getImages(String topic, int page) { return null;}
}

As mentioned we will use Feign client to consume Pexel service. For creating feign client we need to define an interface. Interface will have methods corresponding to endpoint we are going to consume. As we will be consuming two end points, hence we will have two methods.

Also Feign client needs to be annotated with @FeignClient annotation. This annotation will take two params,

  1. Name: name of the client
  2. url: base url of the service

in application.properties define below property

pexel.search.uri=https://api.pexels.com/v1/search

Create either an inner interface within PexelService class or a completely new interface named PexelsClient and annotate it with @FeignClient as below

@FeignClient(name = "data", url = "${pexel.search.uri}")
private interface PexelsClient {

// for fetching images page by page. refer https://www.pexels.com/api/documentation/
@RequestMapping(method = RequestMethod.GET)
PexelsResponse getPhotos(@RequestParam("query") String query,
@RequestParam("per_page") int perPage,
@RequestParam("page") int page);

// for fetching images for a topic. refer https://www.pexels.com/api/documentation/
@RequestMapping(method = RequestMethod.GET)
PexelsResponse getPhotos(@RequestParam("per_page") int perPage,
@RequestParam("page") int page);
}

Spring on run time will create an implementation of feign-client and will create an instance of it. We can get instance of this feign client using @Autowire.

PexelResponse defines Pexel’s response format

Below is POJO classes which represents Pexel’s data format

private static class PexelsResponse {
private List<Photo> photos;

public List<Photo> getPhotos() {
return photos;
}

public void setPhotos(List<Photo> photos) {
this.photos = photos;
}
}

private static class Photo {
private String url;
private String photographer;
@SerializedName("photographer_url")
private String photographerUrl;
private ImageSrc src;

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getPhotographer() {
return photographer;
}

public void setPhotographer(String photographer) {
this.photographer = photographer;
}

public String getPhotographerUrl() {
return photographerUrl;
}

public void setPhotographerUrl(String photographerUrl) {
this.photographerUrl = photographerUrl;
}

public ImageSrc getSrc() {
return src;
}

public void setSrc(ImageSrc src) {
this.src = src;
}
}

private static class ImageSrc {
private String large;

public String getLarge() {
return large;
}

public void setLarge(String large) {
this.large = large;
}
}

Now we have Feign client and we have defined Pexel’s response format. Our feign client will consume Pexel service and will return us response as instance of PexelFormat. Now let’s implement our PexelService’s methods

First of all we need to define how many images we will fetch per page. Like old time, let’s create a constant for this

private static final int MAX_IMAGES_PER_PAGE = 20;

We will obtain instance of feign client using @Autowire

@Autowired
private PexelsClient client;

For Image source let’s create a constant

private static final String PEXELS = "Pexels";

Below is implementation of getImage by page, (refer to the comments for explanation)

@Override
public List<Image> getImages(int page) {
// Returns images in pexel's format
PexelsResponse response = client.getPhotos(MAX_IMAGES_PER_PAGE, page);
List<Image> images = new ArrayList<>();
//Iterate through all pages
for(Photo photo : response.getPhotos()) {
// Create an instance of Image using each photo and add to list of Images
Image image = new Image(PEXELS, photo.getPhotographer(),
photo.getSrc().getLarge(), photo.getPhotographerUrl());
images.add(image);
}
// Return image list
return images;
}

Similarly we will implement getImages for topics as well

@Override
public List<Image> getImages(String topic, int page) {
PexelsResponse response = client.getPhotos(topic, MAX_IMAGES_PER_PAGE, page);
List<Image> images = new ArrayList<>();
for(Photo photo : response.getPhotos()) {
Image image = new Image(PEXELS, photo.getPhotographer(),
photo.getSrc().getLarge(), photo.getPhotographerUrl());
images.add(image);
}
return images;
}

This completes our PexelImageService. But we are still left with one part. With each pexel request we need to send our api key. The best way to include mandatory information in request is request interceptors. Feign client provides a convenient way of registering a request interceptor. All you need to do is to implement feign.RequestIntercptor and annotate it as @Component (spring component)

This interceptor will intercept each feign client’s request and make necessary modification (if required). Create a class PexelRequestInterceptor and implement feign.RequestInterceptor method

@Component
public class PexelRequestInterceptor implements RequestInterceptor {

@Value("${pexel.api.key}")
private String apiKey; // will fetch pexel's api key from Applictaion.properties
private static final String AUTHORIZATION = "Authorization";


@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(AUTHORIZATION, apiKey);
//required as Pexel expect requests from a browser
requestTemplate.header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 " +
"(KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
}
}

Add your Pexels api key you obtained in application.property file with property name pexel.api.key

By now we should be able to fetch images from pexel’s server using instance of PexelImageService.

Let’s create an endpoint in Our QuoteResource rest controller and autowire instance of PexelImageService as type of ImageService

@RestController
public class QuotesResource {

@Autowired
private QuotesService service;

@Autowired
private ImageService imageService; // instance of PexelImageService

//Quote's endpoint


// Endpoint for fetching images
@GetMapping("/images")
  public List<Image> getImages(@RequestParam("topic") String topic
, @RequestParam(value= "page", required = false, defaultValue = "0") int page) {
if(topic == null || topic.trim().length() == 0) {
return imageService.getImages(page);
}
return imageService.getImages(topic, page);
}
}

Now restart spring application and hit url http://localhost:8080/images?topic=science&page=1

If everything done right, you should receive list of images in response.

Leave a comment

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