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.