RestClient provides a clean and fluent way for calling REST APIs. But when you have multiple APIs to call, it can be cumbersome to write the same boilerplate code for each API. Declarative HTTP Clients provides a clean and declarative way to define the API contract using a Java interface and Spring will generate the implementation for you at runtime.
A declarative HTTP client allows you to define an external API's contract using a Java interface annotated with HTTP metadata (like @GetExchange, @PostExchange). Spring then generates the implementation for you at runtime.
This approach separates the definition of the API from the execution logic, making your code cleaner and easier to test.
Let's build a client for JSONPlaceholder, a free fake API for testing. We want to fetch "posts" from this service.
First, create a Java record to model the data and an interface to define the API operations.
public record Post(Integer id, Integer userId, String title, String body) {} @HttpExchange("/posts") public interface PostService { @GetExchange List<Post> findAll(); @GetExchange("/{id}") Post findById(@PathVariable Integer id); @PostExchange Post create(@RequestBody Post post); }
Notice we use @HttpExchange at the class level to define the base path, and method-level annotations for specific endpoints.
In Spring 7, you can register entire groups of clients using the @ImportHttpServices annotation.
We'll define a configuration class and assign this client to a group named "jsonplaceholder".
@Configuration @ImportHttpServices(group = "jsonplaceholder", types = PostService.class) public class ClientConfig { }
Pro Tip: You can also scan entire packages using
@ImportHttpServices(group = "my-group", basePackages = "com.example.clients").
It is also possible to declare groups programmatically by creating an HTTP Service registrar and then importing it:
public class JsonPlaceholderRegistrar extends AbstractHttpServiceRegistrar { @Override protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) { registry.forGroup("jsonplaceholder").register(PostService.class); } } @Configuration @Import(JsonPlaceholderRegistrar.class) public class ClientConfig { }
Spring Boot 4 integrates seamlessly with this registry. You can configure the base URL and other settings for your group directly in application.properties or application.yml.
# Configure the 'jsonplaceholder' group spring.http.client.service.group.jsonplaceholder.base-url=https://jsonplaceholder.typicode.com spring.http.client.service.group.jsonplaceholder.read-timeout=5s
That's it! Spring creates a bean implementing PostService that you can inject anywhere in your application.
@RestController @RequestMapping("/api/posts") public class PostController { private final PostService postService; public PostController(PostService postService) { this.postService = postService; } @GetMapping public List<Post> getAllPosts() { return postService.findAll(); } }
Real-world applications often talk to multiple services. Spring 7 handles this elegantly using Groups. A "Group" is a logical collection of clients that share the same configuration (Base URL, Authentication, Timeouts).
Let's say we also want to talk to the GitHub API.
Define the Interface:
@HttpExchange("/repos/{owner}/{repo}") public interface GitHubService { @GetExchange("/issues") List<Issue> getIssues(@PathVariable String owner, @PathVariable String repo); }
Register the Group:
@Configuration @ImportHttpServices(group = "jsonplaceholder", types = PostService.class) @ImportHttpServices(group = "github", types = GitHubService.class) public class AppConfig {}
Configure Separately:
# JSONPlaceholder spring.http.client.service.group.jsonplaceholder.base-url=https://jsonplaceholder.typicode.com # GitHub spring.http.client.service.group.github.base-url=https://api.github.com
Sometimes properties aren't enough. You might need to add a custom Authorization header or a specific filter for one group. You can do this using HttpServiceGroupConfigurer.
@Bean public HttpServiceGroupConfigurer githubConfigurer(@Value("${github.token}") String token) { return groups -> groups.filterByName("github") .forEachClient((name, builder) -> { builder.defaultHeader("Authorization", "Bearer " + token); builder.requestInterceptor(new LoggingInterceptor()); }); }
This bean allows you to programmatically customize the underlying RestClient.Builder for any specific group.
Spring Framework 7 makes consuming HTTP APIs incredibly productive.
@ImportHttpServices.application.properties.By organizing clients into Groups, you keep your configuration clean and your code decoupled from the underlying HTTP client implementation.
This guide explores the features and usage of the RestClient introduced in Spring 6, providing a modern and fluent API for making HTTP requests. It demonstrates how to create and customize RestClient instances, make API calls, and handle responses effectively.
Key features of Spring Framework 7 and Spring Boot 4 for Java developers.

Get instant AI-powered summaries of YouTube videos and websites. Save time while enhancing your learning experience.