Backend Development 12 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering API Timeout Configurations in Spring Boot 3: From Transactions to NGINX

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>&lt;dependency&gt;
  &lt;groupId&gt;io.github.resilience4j&lt;/groupId&gt;
  &lt;artifactId&gt;resilience4j-spring-boot3&lt;/artifactId&gt;
  &lt;version&gt;2.2.0&lt;/version&gt;
&lt;/dependency&gt;</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>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-webflux&lt;/artifactId&gt;
&lt;/dependency&gt;</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.

TransactionSpring BootNginxWebClientResilience4jAPI timeout
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.