Most services need to call other services. RestClient, introduced in Spring
Framework 6.1, gives you a fluent synchronous API that replaces the aging
RestTemplate without forcing you into reactive types.
Build a configured client
Create the client as a bean with a base URL and default headers so callers stay concise.
@Configuration
public class ClientConfig {
@Bean
public RestClient paymentsClient(RestClient.Builder builder) {
return builder
.baseUrl("https://payments.internal")
.defaultHeader("Accept", "application/json")
.build();
}
}Make requests fluently
The fluent chain reads like the HTTP call itself. body(Class) deserializes the
response for you.
@Service
public class PaymentGateway {
private final RestClient client;
public Receipt charge(ChargeRequest request) {
return client.post()
.uri("/charges")
.body(request)
.retrieve()
.body(Receipt.class);
}
}Handle errors explicitly
Use onStatus to convert HTTP error codes into domain exceptions instead of
leaking raw status codes upward.
Receipt receipt = client.post()
.uri("/charges")
.body(request)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (req, res) -> {
throw new PaymentRejectedException(res.getStatusCode());
})
.body(Receipt.class);Takeaways
- Prefer
RestClientoverRestTemplatefor new synchronous code. - Configure a base URL and defaults once as a bean.
- Translate HTTP errors into domain exceptions with
onStatus.
Set connect and read timeouts on the underlying request factory — a hung dependency should never hang your service indefinitely.