Mastering API Timeout Configurations in Spring Boot 3: From Transactions to NGINX
This article walks through multiple ways to configure API timeout in Spring Boot 3—including transaction timeouts, Resilience4j TimeLimiter, asynchronous request limits, RestTemplate/RestClient/WebClient HTTP client settings, and NGINX proxy timeouts—providing code samples and practical testing results.
Introduction
Configuring API timeout is crucial for system stability and responsiveness in Spring Boot applications. When a client request cannot be processed promptly due to slow database queries, external service failures, or other reasons, a proper timeout prevents resource exhaustion and performance degradation.
Timeout Configuration Scenarios
Transaction timeout
Resilience4j TimeLimiter protection
Asynchronous request timeout
HTTP client timeout (RestTemplate, RestClient, WebClient)
NGINX proxy timeout
1. Transaction Timeout
Use the @Transactional annotation with the timeout attribute to limit the execution time of a transactional method.
<code>@Transactional(timeout = 1)
public List<User> query() {
this.userRepository.findById(8L).ifPresent(System.out::println);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {}
return this.userRepository.findAll();
}</code>When the method exceeds 1 second, a timeout exception is thrown. Handle it with a global exception handler:
<code>@ExceptionHandler(TransactionTimedOutException.class)
public ResponseEntity<Object> txTimeout(TransactionTimedOutException ex) {
return ResponseEntity.ok("Request timed out: " + ex.getMessage());
}</code>Test controller:
<code>@GetMapping
public ResponseEntity<Object> query() {
return ResponseEntity.ok(this.userService.query());
}</code>Test result:
2. Resilience4j TimeLimiter Protection
Add the Resilience4j dependency:
<code><dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency></code>Annotate the endpoint with @TimeLimiter and provide a fallback method:
<code>@TimeLimiter(name = "queryUser", fallbackMethod = "fallbackQuery")
@GetMapping
public CompletionStage<ResponseEntity<Object>> query() {
return CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {}
return ResponseEntity.ok("success");
});
}
public CompletionStage<ResponseEntity<Object>> fallbackQuery(Throwable e) {
return CompletableFuture.completedStage(ResponseEntity.ok(e.getMessage()));
}</code>Configuration in application.yml :
<code>resilience4j:
timelimiter:
instances:
queryUser:
timeout-duration: 1s</code>Test result:
3. Asynchronous Request Timeout
Configure the timeout for async requests in application.yml :
<code>spring:
mvc:
async:
request-timeout: 1s</code>Async controller example:
<code>@GetMapping("/async")
public Callable<String> async() {
return () -> {
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { return "Task interrupted - " + e.getMessage(); }
return "Async request succeeded";
};
}</code>Test result (timeout after 1 s):
4. HTTP Client Timeout
Three client options are covered.
4.1 RestTemplate
<code>@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.connectTimeout(Duration.ofSeconds(1))
.readTimeout(Duration.ofSeconds(1))
.build();
}</code>Alternative factory‑based configuration:
<code>@Bean
RestTemplate restTemplate() {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
.withConnectTimeout(Duration.ofSeconds(1))
.withReadTimeout(Duration.ofSeconds(1));
return new RestTemplate(ClientHttpRequestFactoryBuilder.detect().build(settings));
}</code>4.2 RestClient (Spring 6.1+)
<code>@Bean
RestClient restClient() {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
.withConnectTimeout(Duration.ofSeconds(1))
.withReadTimeout(Duration.ofSeconds(1));
return RestClient.builder()
.requestFactory(ClientHttpRequestFactoryBuilder.detect().build(settings))
.build();
}</code>4.3 WebClient (recommended)
Add the WebFlux starter dependency:
<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency></code>Configure timeout handlers:
<code>@Bean
WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(1))
.addHandlerLast(new WriteTimeoutHandler(1)));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}</code>Example call with error handling:
<code>private final WebClient webClient;
webClient.get()
.uri("http://localhost:8080/api/users/client")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(ex -> Mono.just("Error: " + ex.getMessage()))
.subscribe(System.out::println);
</code>Test result (read timeout):
4.4 Summary
The above snippets demonstrate how to set connection and read timeouts for the three major HTTP client options in Spring Boot.
5. NGINX Proxy Timeout
Configure timeout values in the NGINX location block:
<code>location / {
proxy_pass http://127.0.0.1:8080;
proxy_connect_timeout 1s; # connection timeout
proxy_send_timeout 1s; # send timeout
proxy_read_timeout 1s; # read timeout
}</code>Custom error page for timeout (504):
<code>error_page 504 /timeout.html;
location = /timeout.html {
root D:/all/html/;
internal;
}</code>Conclusion
By applying the appropriate timeout configuration—whether at the transaction level, via Resilience4j, for asynchronous calls, through HTTP client settings, or at the NGINX proxy—you can ensure that Spring Boot services remain responsive and resilient under adverse conditions.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.