Working with External APIs
1. Creating RestTemplate for HTTP Requests
Example: RestTemplate with timeout and base URL
@Bean
RestTemplate restTemplate(RestTemplateBuilder b) {
return b.connectTimeout(Duration.ofSeconds(2))
.readTimeout(Duration.ofSeconds(5))
.rootUri("https://api.example.com")
.build();
}
// Usage
String json = restTemplate.getForObject("/users/{id}", String.class, 42);
Note: RestTemplate is in maintenance mode. Prefer RestClient
(Spring 6.1+) or WebClient.
2. Using WebClient for Reactive HTTP Requests
Example: WebClient for reactive HTTP calls
@Bean
WebClient webClient(WebClient.Builder b) {
return b.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.USER_AGENT, "myapp/1.0")
.build();
}
Mono<UserDto> user = webClient.get().uri("/users/{id}", 42)
.retrieve().bodyToMono(UserDto.class);
3. Using RestClient for Modern HTTP Requests
Example: RestClient for synchronous HTTP calls
@Bean
RestClient restClient(RestClient.Builder b) {
return b.baseUrl("https://api.example.com").build();
}
UserDto u = restClient.get().uri("/users/{id}", 42)
.retrieve().body(UserDto.class);
4. Configuring Connection Timeouts
| Setting |
Default |
| Connect timeout |
None — set explicitly |
| Read/response timeout |
None — set explicitly |
| Connection pool size |
Depends on client (Apache HC, Reactor Netty) |
5. Handling HTTP Response Codes
Example: Handle 4xx and 5xx with custom exceptions
UserDto u = restClient.get().uri("/users/{id}", 42)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(req, res) -> { throw new NotFoundException("user " + 42); })
.onStatus(HttpStatusCode::is5xxServerError,
(req, res) -> { throw new UpstreamException(res.getStatusCode().value()); })
.body(UserDto.class);
6. Using RestTemplate Interceptors
ClientHttpRequestInterceptor auth = (req, body, exec) -> {
req.getHeaders().setBearerAuth(tokenProvider.get());
return exec.execute(req, body);
};
restTemplate.setInterceptors(List.of(auth));
7. Implementing Retry Logic for Failed Requests
Example: Retry with backoff and recover fallback
@Retryable(retryFor = {ResourceAccessException.class},
maxAttempts = 3, backoff = @Backoff(delay = 200, multiplier = 2))
public UserDto fetchUser(Long id) {
return restClient.get().uri("/users/{id}", id).retrieve().body(UserDto.class);
}
@Recover public UserDto fallback(ResourceAccessException ex, Long id) { return UserDto.unknown(id); }
8. Using Circuit Breaker with External APIs
Example: Circuit breaker with fallback method
@CircuitBreaker(name = "userApi", fallbackMethod = "fallback")
public UserDto fetch(Long id) {
return restClient.get().uri("/users/{id}", id).retrieve().body(UserDto.class);
}
public UserDto fallback(Long id, Throwable t) { return UserDto.unknown(id); }
9. Handling API Authentication
| Scheme |
Header / Method |
| Bearer |
Authorization: Bearer <token> |
| Basic |
Authorization: Basic base64(user:pass) |
| API Key |
X-API-Key: ... or query param |
| OAuth2 client credentials |
Spring Security OAuth2 client |
| mTLS |
Configure SSLContext on HttpClient |
10. Parsing JSON Responses (Jackson)
Example: Deserialize generic list response
List<UserDto> users = restClient.get().uri("/users")
.retrieve()
.body(new ParameterizedTypeReference<List<UserDto>>() {});
11. Testing External API Integration (MockRestServiceServer)
Example: Mock REST server in test
@RestClientTest(UserClient.class)
class UserClientTests {
@Autowired UserClient client;
@Autowired MockRestServiceServer server;
@Test void fetch() {
server.expect(requestTo("https://api.example.com/users/1"))
.andRespond(withSuccess("{\"id\":1,\"name\":\"Alice\"}", MediaType.APPLICATION_JSON));
assertEquals("Alice", client.fetch(1L).name());
}
}
12. Using Feign Client for Declarative REST Clients
Example: Feign declarative HTTP client
@FeignClient(name = "users", url = "https://api.example.com")
public interface UserApi {
@GetMapping("/users/{id}") UserDto get(@PathVariable Long id);
@PostMapping("/users") UserDto create(@RequestBody NewUser body);
}
// @EnableFeignClients on @SpringBootApplication