Configure RestTemplate with RestTemplateBuilder in Spring Boot

Configure RestTemplateBuilder in Spring Boot 4.1.2 with a base URI, headers, timeouts, a global customizer, and a tested replacement builder.

Spring Framework

Spring Boot provides a preconfigured RestTemplateBuilder, which creates a RestTemplate with Boot’s HTTP client and message conversion settings. Configure that builder where the client is created when only one remote service needs a setting. Use a RestTemplateCustomizer for a setting shared by every builder-created template.

This tutorial uses Spring Boot 4.1.2 and Java 17 or later. A checkout application requests delivery estimates from a separate HTTP service. Its client needs the service’s base URL, an application header, and connection and read timeouts. We will build that client, then see how a platform-wide header and a replacement builder change the configuration scope.

1. Add the REST Client Dependencies

The Spring Boot REST client starter provides the auto-configured builder and the RestTemplate support. The test starter includes MockRestServiceServer, which can inspect a client’s outgoing request without starting a second application.

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.1.2</version>
  <relativePath/>
</parent>

<properties><java.version>17</java.version></properties>

<dependencies>
  <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-restclient</artifactId></dependency>
  <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-restclient-test</artifactId><scope>test</scope></dependency>
</dependencies>

Spring Boot creates a RestTemplateBuilder bean, but it does not create one general-purpose RestTemplate bean. Each client can therefore use the injected builder to construct a template with settings appropriate to its remote service. In Spring Boot 4, the builder’s import is org.springframework.boot.restclient.RestTemplateBuilder.

2. Configure One Delivery Client

The delivery service accepts GET /quotes/{postalCode} and returns JSON such as {"postalCode":"90210","days":2}. The checkout client maps that response to a DeliveryQuote record. It adds X-Application: checkout to its requests so the delivery service can identify the caller.

@Service
public class DeliveryQuoteClient {
    private final RestTemplate restTemplate;

    public DeliveryQuoteClient(RestTemplateBuilder builder,
                               @Value("${delivery.base-url}") String baseUrl) {
        this.restTemplate = builder
                .baseUri(baseUrl)
                .defaultHeader("X-Application", "checkout")
                .connectTimeout(Duration.ofSeconds(2))
                .readTimeout(Duration.ofSeconds(3))
                .build();
    }

    public DeliveryQuote quote(String postalCode) {
        return restTemplate.getForObject(
                "/quotes/{postalCode}", DeliveryQuote.class, postalCode);
    }

    public record DeliveryQuote(String postalCode, int days) {}
}

Set delivery.base-url=https://delivery.example.com in application configuration for the actual service. The code passes /quotes/{postalCode} to the String overload of getForObject(). Because that request path begins with /, baseUri() prefixes the configured service URL. The builder’s defaultHeader() adds the application header unless a request already supplies that header.

The two timeout values serve different stages: the connection timeout limits establishing the connection, while the read timeout limits waiting for response data after the request is sent. See the RestTemplate timeout example for a delayed-response test. The builder returns a new builder after each configuration method, so these settings stay with this client rather than changing every injected builder in the application.

3. Add a Setting to Every Builder-Created Client

Suppose the platform requires an X-Platform header on every outbound request. Repeating that header in each client makes it easy to omit. A RestTemplateCustomizer bean lets Spring Boot apply one additive rule to each RestTemplate created by its auto-configured builder.

@Configuration(proxyBeanMethods = false)
public class PlatformClientConfiguration {
    @Bean
    RestTemplateCustomizer platformHeader() {
        return template -> template.getInterceptors().add((request, body, execution) -> {
            request.getHeaders().set("X-Platform", "storefront");
            return execution.execute(request, body);
        });
    }
}

The interceptor adds the header immediately before the HTTP request is executed. It is attached to templates built from Boot’s configured builder; a manually constructed new RestTemplate() does not receive it. The delivery client still adds its own X-Application header, so the two settings coexist. For more on the interception hook and what it may log safely, see RestTemplate request and response interceptors.

4. Replace the Builder Only When Necessary

Declaring a RestTemplateBuilder bean replaces Spring Boot’s auto-configured builder. That has a wider effect than configuring one client or adding a customizer. If an application needs a shared builder default, pass a new builder through RestTemplateBuilderConfigurer before adding the setting. The configurer reapplies Boot’s defaults and discovered customizers.

@Configuration(proxyBeanMethods = false)
@Profile("replace-builder")
public class ReplacementBuilderConfiguration {
    @Bean
    RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
        return configurer.configure(new RestTemplateBuilder())
                .connectTimeout(Duration.ofSeconds(2))
                .readTimeout(Duration.ofSeconds(3));
    }
}

The runnable project guards this configuration with the replace-builder profile, so the ordinary application uses Boot’s default builder. A separate test activates the profile and confirms that the platform customizer still adds X-Platform. Without the configurer, a replacement builder does not automatically receive those customizers.

Requirement Configuration Scope
Base URL, headers or timeouts for one service Customize the injected RestTemplateBuilder in that client That client’s template
A shared additive rule for builder-created templates Declare a RestTemplateCustomizer bean All templates built with Boot’s builder
Replace Boot’s builder defaults Declare a builder bean through RestTemplateBuilderConfigurer Every consumer of that builder

The first approach is usually enough for a service-specific client. The other two are useful when the requirement is genuinely shared across clients.

5. Verify the Request That the Builder Produces

The first test binds MockRestServiceServer to the constructed template. It expects the base URI, postal code, HTTP method, client-specific header and platform-wide header. The mock server then returns JSON that the client must map to DeliveryQuote.

var server = MockRestServiceServer.bindTo(client.restTemplate()).build();
server.expect(requestTo("https://delivery.example.test/quotes/90210"))
        .andExpect(method(HttpMethod.GET))
        .andExpect(header("X-Application", "checkout"))
        .andExpect(header("X-Platform", "storefront"))
        .andRespond(withSuccess(
                "{\"postalCode\":\"90210\",\"days\":2}",
                MediaType.APPLICATION_JSON));

assertThat(client.quote("90210"))
        .isEqualTo(new DeliveryQuoteClient.DeliveryQuote("90210", 2));
server.verify();

Both project tests pass on Spring Boot 4.1.2: one for Boot’s default builder and one for the replacement builder profile. The second test confirms that the shared X-Platform customizer survives the replacement because the configurer was used. These tests check request construction and JSON mapping; they do not simulate slow connections or claim that the timeout durations were reached.

6. Frequently Asked Questions

6.1. Should I inject RestTemplateBuilder or RestTemplate?

Inject RestTemplateBuilder when you need to create a client with its own settings. Spring Boot supplies the builder but no universal RestTemplate bean. You can declare a RestTemplate bean yourself if several components must share exactly the same configured client.

6.2. Should Boot 4 code use rootUri()?

For Spring Boot 4.1.2, use baseUri() for this purpose. The current Javadoc marks rootUri() deprecated for removal in 4.3.0. baseUri() applies to String URL arguments beginning with /; it does not rewrite an already constructed URI argument.

6.3. Why not call new RestTemplate() in each service?

A manually constructed template can make HTTP calls, but it does not receive the settings Spring Boot applies to its builder or the registered RestTemplateCustomizer beans. Use the auto-configured builder when the application depends on those Boot defaults and shared customizations.

6.4. Should a new application choose RestTemplate?

The Spring Framework REST client reference describes RestTemplate as deprecated in favor of RestClient. Continue using this builder where an existing client already uses the RestTemplate API; for a new synchronous client, start with RestClient and its Boot-configured builder.

7. Conclusion

Build the delivery client from Spring Boot’s injected RestTemplateBuilder and keep its base URL, application header and timeouts with that client. A RestTemplateCustomizer adds a rule shared by builder-created templates. If you replace the builder bean, use RestTemplateBuilderConfigurer to retain Boot’s setup. The Spring Boot REST client reference documents these configuration scopes for the current release.

The complete runnable code is in the RestTemplateBuilder project folder.

Leave a Comment

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.