Implementing API Idempotency with Spring Boot, Redis, and Custom Annotations

This article explains the concept of idempotency, presents several strategies to achieve it, and provides a complete Spring Boot implementation using Redis, a custom @AutoIdempotent annotation, token generation and verification, interceptor configuration, and test cases to ensure only the first request succeeds.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Implementing API Idempotency with Spring Boot, Redis, and Custom Annotations

In real-world development, an exposed API may receive many requests, so ensuring idempotency—where multiple executions have the same effect as a single execution—is essential. Common techniques include unique database indexes, token mechanisms, pessimistic/optimistic locks, and pre‑check‑then‑execute logic.

Redis can be used to implement automatic idempotency. First, a Redis server is set up and accessed via Spring Boot’s RedisTemplate. The following utility class provides basic operations such as set, setEx, exists, get, and remove:

@Component
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;
    public boolean set(final String key, Object value) { /* ... */ }
    public boolean setEx(final String key, Object value, Long expireTime) { /* ... */ }
    public boolean exists(final String key) { return redisTemplate.hasKey(key); }
    public Object get(final String key) { /* ... */ }
    public boolean remove(final String key) { /* ... */ }
}

A custom annotation @AutoIdempotent is defined to mark methods that require automatic idempotency:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIdempotent {}

The token service interface declares methods for creating and checking tokens:

public interface TokenService {
    String createToken();
    boolean checkToken(HttpServletRequest request) throws Exception;
}

Its implementation generates a UUID token, stores it in Redis with a configurable expiration, and validates the token on subsequent requests, removing it after successful verification:

@Service
public class TokenServiceImpl implements TokenService {
    @Autowired
    private RedisService redisService;
    public String createToken() { /* generate UUID, store in Redis */ }
    public boolean checkToken(HttpServletRequest request) throws Exception { /* retrieve, validate, delete */ }
}

The interceptor configuration adds AutoIdempotentInterceptor to the Spring MVC interceptor chain. The interceptor checks whether the handler method is annotated with @AutoIdempotent and, if so, invokes TokenService.checkToken. On failure, it writes a JSON error response.

@Component
public class AutoIdempotentInterceptor implements HandlerInterceptor {
    @Autowired
    private TokenService tokenService;
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) return true;
        Method method = ((HandlerMethod) handler).getMethod();
        AutoIdempotent ann = method.getAnnotation(AutoIdempotent.class);
        if (ann != null) {
            try { return tokenService.checkToken(request); }
            catch (Exception ex) { /* write JSON error */ throw ex; }
        }
        return true;
    }
    // postHandle and afterCompletion omitted for brevity
    private void writeReturnJson(HttpServletResponse response, String json) throws Exception { /* ... */ }
}

A Web configuration class registers the interceptor:

@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
    @Resource
    private AutoIdempotentInterceptor autoIdempotentInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(autoIdempotentInterceptor);
        super.addInterceptors(registry);
    }
}

Test cases demonstrate the workflow: a client first calls /get/token to obtain a token, then calls a protected endpoint annotated with @AutoIdempotent. The first request succeeds; subsequent requests with the same token are rejected as duplicate operations.

In summary, the blog shows how to combine Spring Boot, Redis, custom annotations, and interceptors to achieve elegant API idempotency, preventing duplicate data modifications and reducing unnecessary load.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaredisSpring BootInterceptorannotationsIdempotency
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

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.