RU | EN | DE

1. RestTemplate (Synchronous Client)

πŸ“Œ Old, but still used. Considered deprecated in Spring 6, but it’s important to know. πŸ“„ Bean configuration:

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .setConnectTimeout(Duration.ofSeconds(3))
                .setReadTimeout(Duration.ofSeconds(5))
                .build();
    }
}

πŸ“„ Usage examples:

@Service
@RequiredArgsConstructor
public class WeatherService {
    private final RestTemplate restTemplate;
    public WeatherDto getWeather(String city) {
        String url = "https://api.weather.com/city/" + city;
        return restTemplate.getForObject(url, WeatherDto.class);
    }
    public void createUser(UserDto user) {
        restTemplate.postForEntity("http://localhost:8080/api/users", user, Void.class);
    }
}

2. WebClient (Reactive Client)

πŸ“Œ Modern alternative to RestTemplate. Supports asynchronicity and reactivity.

πŸ“„ Bean configuration:

@Configuration
public class WebClientConfig {
    @Bean
    public WebClient webClient(WebClient.Builder builder) {
        return builder.baseUrl("https://api.weather.com")
                       .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                       .build();
    }
}

πŸ“„ Usage:

@Service
@RequiredArgsConstructor
public class WeatherService {
    private final WebClient webClient;
    public Mono<WeatherDto> getWeather(String city) {
        return webClient.get()
                       .uri("/city/{city}", city)
                       .retrieve()
                       .bodyToMono(WeatherDto.class);
    }
 
    public Flux<UserDto> getUsers() {
        return webClient.get()
                       .uri("/api/users")
                       .retrieve()
                       .bodyToFlux(UserDto.class);
    }
}
 
πŸ“Œ Mono<T> = a single object (analogous to Optional),
Flux<T> = a stream of objects (like List).

3. OpenFeign (Declarative Client)

πŸ“Œ If you have microservices β†’ the best choice. No need to manually write RestTemplate or WebClient.

πŸ“„ Dependency:

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

πŸ“„ Integration:

@SpringBootApplication
@EnableFeignClients
public class DemoApplication {}

πŸ“„ Interface client:

@FeignClient(name = "userClient", url = "http://localhost:8080/api/users")
public interface UserClient {
    @GetMapping("/{id}")
    UserDto getUser(@PathVariable Long id);
    @PostMapping
    UserDto createUser(@RequestBody UserDto dto);
}

πŸ“„ Usage:

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserClient client;
    public UserDto get(Long id) {
        return client.getUser(id);
    }
}

4. Retry & Timeout (Resilience4j)

πŸ“Œ Sometimes an external API fails β†’ you need to retry or limit the waiting time.

πŸ“„ Dependency:

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
</dependency>

πŸ“„ Configuration:

resilience4j.retry:
    instances:
        apiRetry:
            maxAttempts: 3
            waitDuration: 2s

πŸ“„ Usage:

@Service
public class ExternalService {
    @Retry(name = "apiRetry")
    public String callApi() {
        if (Math.random() > 0.7) {
            return "Success!";
        }
        throw new RuntimeException("API failed");
    }
}

5. Circuit Breaker (Fuse)

πŸ“Œ To prevent overloading the service with constant requests if it’s already failing.

πŸ“„ Configuration:

resilience4j.circuitbreaker:
    instances:
        apiBreaker:
            failureRateThreshold: 50
            waitDurationInOpenState: 10s
            slidingWindowSize: 10

πŸ“„ Usage:

@Service
public class ExternalService {
    @CircuitBreaker(name = "apiBreaker", fallbackMethod = "fallback")
    public String callApi() {
        throw new RuntimeException("API error");
    }
    public String fallback(Throwable t) {
        return "Fallback response";
    }
}

6. Async API calls (Parallel requests)

CompletableFuture:

@Async
public CompletableFuture<UserDto> fetchUser(Long id) {
    return CompletableFuture.supplyAsync(() -> client.getUser(id));
}

WebClient:

Flux<UserDto> users = webClient.get()
    .uri("/api/users")
    .retrieve()
    .bodyToFlux(UserDto.class);

7. Best Practices

  • βœ… For synchronous APIs β†’ RestTemplate (deprecated, but still encountered).
  • βœ… For asynchronous/reactive services β†’ WebClient.
  • βœ… For microservices β†’ Feign.
  • βœ… Add retry, timeout, circuit breaker using Resilience4j.
  • βœ… Never make external calls without timeouts.
  • βœ… Log all requests/responses (Spring Boot Sleuth or custom LoggingInterceptor).