Skip to content

NiMv1/spring-boot-starter-idempotency

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Spring Boot Starter Idempotency

Java Spring Boot Redis License

πŸ”’ Spring Boot starter for idempotent request handling with Redis support

English | Русский

✨ Features

  • Simple annotation @Idempotent for methods
  • SpEL expressions for key generation
  • Configurable TTL per method
  • Automatic key removal on error (retry-friendly)
  • Full integration with Spring Boot 3.x

πŸš€ Quick Start

1. Add dependency

<dependency>
    <groupId>io.github.nimv1</groupId>
    <artifactId>spring-boot-starter-idempotency</artifactId>
    <version>1.0.0</version>
</dependency>

2. Configure Redis

spring:
  data:
    redis:
      host: localhost
      port: 6379

3. Use the annotation

@RestController
@RequestMapping("/api/payments")
public class PaymentController {

    @PostMapping
    @Idempotent(key = "#request.transactionId", ttl = 24, timeUnit = TimeUnit.HOURS)
    public PaymentResponse processPayment(@RequestBody PaymentRequest request) {
        // This method will be executed only once for each transactionId
        return paymentService.process(request);
    }
}

πŸ“– Documentation

@Idempotent Annotation

Parameter Type Default Description
key String required SpEL expression for key generation
ttl long 1 Key time-to-live
timeUnit TimeUnit HOURS Time unit for TTL
prefix String "idempotent:" Key prefix in Redis
message String "Duplicate request detected" Error message

SpEL Expressions

// Parameter by name
@Idempotent(key = "#transactionId")

// Nested property
@Idempotent(key = "#request.orderId")

// Combined parameters
@Idempotent(key = "#userId + ':' + #orderId")

// Parameter by index
@Idempotent(key = "#p0.id")

Configuration

idempotency:
  enabled: true              # Enable/disable (default true)
  default-ttl: 1             # Default TTL
  default-time-unit: HOURS   # Default time unit
  key-prefix: "idempotent:"  # Redis key prefix

Error Handling

On duplicate request, IdempotencyException is thrown:

@ExceptionHandler(IdempotencyException.class)
public ResponseEntity<ErrorResponse> handleIdempotency(IdempotencyException ex) {
    return ResponseEntity
            .status(HttpStatus.CONFLICT)
            .body(new ErrorResponse(ex.getMessage(), ex.getIdempotencyKey()));
}

πŸ”§ How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Request   │────▢│ IdempotencyAspect│────▢│    Redis    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚                       β”‚
                            β–Ό                       β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
                    β”‚ Key exists?   β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚         β”‚
                     Yes        No
                      β”‚         β”‚
                      β–Ό         β–Ό
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚  Throw    β”‚  β”‚ Execute methodβ”‚
              β”‚ Exception β”‚  β”‚ & save key    β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“ Usage Examples

Payment Service

@Service
public class PaymentService {

    @Idempotent(key = "#payment.transactionId", ttl = 24, timeUnit = TimeUnit.HOURS)
    public PaymentResult processPayment(Payment payment) {
        // Safe from duplicate processing
        return gateway.charge(payment);
    }
}

Notification Service

@Service
public class NotificationService {

    @Idempotent(key = "#userId + ':' + #eventType", ttl = 5, timeUnit = TimeUnit.MINUTES)
    public void sendNotification(String userId, String eventType, String message) {
        // User will receive only one notification per 5 minutes
        emailService.send(userId, message);
    }
}

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ‘€ Author

NiMv1 - GitHub | Portfolio

About

Spring Boot starter for idempotent request handling with Redis support

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages