In this tutorial, we’ll demonstrate how easy it is to create a REST client with Spring boot to consume a REST api endpoint using declarative style
First, let’s create REST service using nodejs following the example here: https://leftsidemonitor.com/nodejs-express-create-project/
router.get('/api/users', function(req, res, next) { let users = [{ 'name': 'Hulk', 'skill': 'Smash' }, { 'name': 'Thor', 'skill': 'thunder' }]; res.end(JSON.stringify(users)); });
Let’s test this microservice:
curl http://localhost:3000/api/users [ { "name": "Hulk", "skill": "Smash" }, { "name": "Thor", "skill": "thunder" } ]
Now in your Spring boot project, let’s define a model that is corresponded to the above data model
package com.leftsidemonitor.testhero.model; import lombok.Data; @Data public class User { private String name; private String skill; }
Before we go ahead and create a REST client, the following packages are needed, please update your pom.xml
if you’re using maven
## Add to pom.xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>11.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-jackson</artifactId> <version>11.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-slf4j</artifactId> <version>11.0</version> </dependency> <dependencyManagement> <dependencies> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-bom</artifactId> <version>${testcontainers.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Now is a simple REST client using Spring Cloud Feign library
package com.leftsidemonitor.testhero.client; import com.leftsidemonitor.testhero.model.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @FeignClient(name = "users", url = "${remote.api.user.client}") public interface UserServiceClient { @GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE) List<User> getUsers(); }
Now let’s create a simple Service class that uses this REST client and also modify the receiving data:
package com.leftsidemonitor.testhero.service; import com.leftsidemonitor.testhero.client.UserServiceClient; import com.leftsidemonitor.testhero.model.User; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service @AllArgsConstructor public class UserService { private UserServiceClient userServiceClient; public List<User> getUsersFromRemoteAPI() { List<User> remoteUsers = userServiceClient.getUsers(); return remoteUsers.stream() .peek(user -> user.setName("From a galaxy far away: " + user.getName())) .collect(Collectors.toList()); } }
And here is a controller class to bind all of these together
package com.leftsidemonitor.testhero.web; import com.leftsidemonitor.testhero.model.User; import com.leftsidemonitor.testhero.service.UserService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/remote") @Slf4j @AllArgsConstructor public class UserResource { private UserService userService; @GetMapping("/users") public List<User> getRemoteUsers() { return userService.getUsersFromRemoteAPI(); } }
Important: in order for FeignClient to work, there must be a simple configuration as following:
package com.leftsidemonitor.testhero.config; import com.fasterxml.jackson.databind.ObjectMapper; import feign.Logger; import feign.codec.Decoder; import feign.codec.Encoder; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableFeignClients(basePackages = "com.leftsidemonitor.testhero.client") public class FeignConfig { private static final ObjectMapper mapper = new ObjectMapper(); private final ObjectFactory<HttpMessageConverters> messageConverters; public FeignConfig(ObjectFactory<HttpMessageConverters> messageConverters) { this.messageConverters = messageConverters; } @Bean public Decoder feignDecoder() { return new JacksonDecoder(mapper); } @Bean public Encoder feignEncoder() { return new JacksonEncoder(mapper); } @Bean Logger.Level feignLoggerLevel() { return Logger.Level.BASIC; } }
remote.api.user.client=http://localhost:3000/api
Now let’s test:
curl http://localhost:8080/remote/users [ { "name": "From a galaxy far away: Hulk", "skill": "Smash" }, { "name": "From a galaxy far away: Thor", "skill": "thunder" } ]