With RestTemplate, WebClient, and FeignClient (Headers, Clean Code, CRUD)
In modern microservice or distributed system architecture, it's crucial to know how to consume REST APIs effectively. In this guide, we’ll explore three powerful ways to consume APIs in Spring Boot:
- 🔗
RestTemplate
– classic and synchronous. - ⚡
WebClient
– reactive and non-blocking. - 🤝
FeignClient
– declarative and elegant.
We’ll build a common User
model and implement full CRUD operations with all HTTP headers (e.g., Authorization
, Content-Type
, Accept
).
🧩 Common User Model
public class User {
private Long id;
private String name;
private String email;
// Constructors, Getters, Setters
}
1️⃣ RestTemplate – Synchronous HTTP Client
✅ Use when you're building traditional apps with blocking I/O.
@Service
public class UserRestTemplateService {
private final RestTemplate restTemplate = new RestTemplate();
private final String BASE_URL = "http://localhost:8081/users";
private HttpHeaders getDefaultHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("Authorization", "Bearer your_token_here");
return headers;
}
public User getUser(Long id) {
HttpEntity<Void> entity = new HttpEntity<>(getDefaultHeaders());
ResponseEntity<User> response = restTemplate.exchange(BASE_URL + "/" + id, HttpMethod.GET, entity, User.class);
return response.getBody();
}
public User[] getAllUsers() {
HttpEntity<Void> entity = new HttpEntity<>(getDefaultHeaders());
ResponseEntity<User[]> response = restTemplate.exchange(BASE_URL, HttpMethod.GET, entity, User[].class);
return response.getBody();
}
public User createUser(User user) {
HttpEntity<User> entity = new HttpEntity<>(user, getDefaultHeaders());
ResponseEntity<User> response = restTemplate.exchange(BASE_URL, HttpMethod.POST, entity, User.class);
return response.getBody();
}
public void updateUser(Long id, User user) {
HttpEntity<User> entity = new HttpEntity<>(user, getDefaultHeaders());
restTemplate.exchange(BASE_URL + "/" + id, HttpMethod.PUT, entity, Void.class);
}
public void deleteUser(Long id) {
HttpEntity<Void> entity = new HttpEntity<>(getDefaultHeaders());
restTemplate.exchange(BASE_URL + "/" + id, HttpMethod.DELETE, entity, Void.class);
}
}
2️⃣ WebClient – Reactive and Non-Blocking
✅ Use when building reactive microservices or high-concurrency apps.
@Service
public class UserWebClientService {
private final WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:8081/users")
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("Accept", MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("Authorization", "Bearer your_token_here")
.build();
public Mono<User> getUser(Long id) {
return webClient.get()
.uri("/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
public Flux<User> getAllUsers() {
return webClient.get()
.retrieve()
.bodyToFlux(User.class);
}
public Mono<User> createUser(User user) {
return webClient.post()
.bodyValue(user)
.retrieve()
.bodyToMono(User.class);
}
public Mono<Void> updateUser(Long id, User user) {
return webClient.put()
.uri("/{id}", id)
.bodyValue(user)
.retrieve()
.bodyToMono(Void.class);
}
public Mono<Void> deleteUser(Long id) {
return webClient.delete()
.uri("/{id}", id)
.retrieve()
.bodyToMono(Void.class);
}
}
3️⃣ FeignClient – Declarative REST Client
✅ Best for microservices and clean architecture using Spring Cloud OpenFeign.
✏️ Step 1: Add dependency
org.springframework.cloud
spring-cloud-starter-openfeign
✏️ Step 2: Enable Feign support
@SpringBootApplication
@EnableFeignClients
public class Application { }
✏️ Step 3: Configuration for headers
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Content-Type", "application/json");
requestTemplate.header("Accept", "application/json");
requestTemplate.header("Authorization", "Bearer your_token_here");
};
}
}
✏️ Step 4: Define FeignClient interface
@FeignClient(name = "user-client", url = "http://localhost:8081", configuration = FeignConfig.class)
public interface UserFeignClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable Long id);
@GetMapping("/users")
User[] getAllUsers();
@PostMapping("/users")
User createUser(@RequestBody User user);
@PutMapping("/users/{id}")
void updateUser(@PathVariable Long id, @RequestBody User user);
@DeleteMapping("/users/{id}")
void deleteUser(@PathVariable Long id);
}
✏️ Step 5: Use the client
@Service
public class UserFeignService {
private final UserFeignClient client;
public UserFeignService(UserFeignClient client) {
this.client = client;
}
public User getUser(Long id) {
return client.getUser(id);
}
public User[] getAllUsers() {
return client.getAllUsers();
}
public User createUser(User user) {
return client.createUser(user);
}
public void updateUser(Long id, User user) {
client.updateUser(id, user);
}
public void deleteUser(Long id) {
client.deleteUser(id);
}
}
🧠 When to Use What?
Feature | RestTemplate ✅ | WebClient ⚡ | FeignClient 🤝 |
---|---|---|---|
Synchronous | ✅ Yes | 🚫 No (reactive only) | ✅ Yes |
Reactive Support | 🚫 No | ✅ Yes | 🚫 No |
Simplicity | ✅ Moderate | 🚫 Complex for blocking | ✅ Very Clean |
Headers | ✅ Manual | ✅ Easy | ✅ Easy via Config |
Best Use Case | Legacy apps | Reactive APIs | Microservices |
✅ Conclusion
You now have a full CRUD implementation for consuming APIs using all three major techniques in Spring Boot:
- 💡
RestTemplate
for blocking applications - 🔄
WebClient
for reactive systems - 🧼
FeignClient
for clean declarative usage
👉 All include proper headers, clean coding practices, and a reusable structure.
Use this as your go-to reference when working with any third-party or internal REST APIs.