In RESTful API design, idempotency is a crucial concept that ensures safety and consistency, especially for POST, PUT, or DELETE operations. It guarantees that repeating the same request multiple times has the same effect as making it once. This is essential in distributed systems where network retries are common.
In this blog, weโll explore:
- โ What is idempotency?
- ๐ Why idempotency matters in API design
- ๐ ๏ธ How to implement idempotency in Spring Boot
- ๐ Using
Idempotency-Key - ๐ก Best practices
- ๐ฆ Example code snippets
๐ค What is Idempotency?
An API is idempotent if multiple identical requests result in the same server state and same response.
| HTTP Method | Idempotent? | Description |
|---|---|---|
| GET | โ | Safe, no changes |
| PUT | โ | Overwrites the resource |
| DELETE | โ | Removes the resource (multiple calls have the same result) |
| POST | โ (by default) | Usually creates new resources (can be made idempotent) |
๐ Why Idempotency Matters
Imagine a money transfer API that retries due to a timeout. Without idempotency, it may deduct the amount multiple times. Idempotency ensures:
- Data consistency
- Retry safety
- Easier debugging
- Enhanced user trust
๐ ๏ธ How to Design Idempotent APIs in Spring Boot
โ 1. Use Idempotency Key (For POST requests)
- Clients send a unique
Idempotency-Keyin the header. - Server stores the response associated with the key.
- Repeated requests with the same key return the stored response without executing the logic again.
๐ Sample Header
POST /api/payments
Idempotency-Key: abc123
๐งช Example Implementation in Spring Boot
Step 1: Create Entity for Storing Keys
@Entity
public class IdempotencyRecord {
@Id
private String key;
private String responseBody;
private int statusCode;
}
Step 2: Create Repository
@Repository
public interface IdempotencyRepository extends JpaRepository<IdempotencyRecord, String> {}
Step 3: Create Filter or Interceptor
@Component
public class IdempotencyInterceptor implements HandlerInterceptor {
@Autowired
private IdempotencyRepository repository;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String key = request.getHeader("Idempotency-Key");
if (key != null && repository.findById(key).isPresent()) {
IdempotencyRecord record = repository.findById(key).get();
response.setStatus(record.getStatusCode());
response.getWriter().write(record.getResponseBody());
return false; // Stop processing
}
return true; // Continue
}
}
Step 4: Store Response After Processing
In your controller or service layer:
@PostMapping("/payments")
public ResponseEntity<String> makePayment(@RequestBody PaymentRequest request,
@RequestHeader("Idempotency-Key") String key) {
String response = "Payment of " + request.getAmount() + " successful!";
// Save idempotent record
IdempotencyRecord record = new IdempotencyRecord();
record.setKey(key);
record.setResponseBody(response);
record.setStatusCode(200);
repository.save(record);
return ResponseEntity.ok(response);
}
โ ๏ธ Caution
- TTL (Time-to-live): Store keys temporarily to prevent DB bloat.
- Uniqueness: Ensure
Idempotency-Keyis truly unique per action. - Side effects: Avoid non-idempotent side-effects like sending emails in retries.
๐ง Best Practices
- Make APIs idempotent by default where possible.
- Return consistent response codes.
- Store metadata for debugging.
- Educate API consumers on using
Idempotency-Key.
๐งฉ Real-World Use Cases
- Payment processing (Stripe, Razorpay)
- Booking engines
- Order creation
- Email subscriptions
โ Conclusion
Designing idempotent APIs is a must for building robust, production-grade systems. It prevents data duplication, race conditions, and unexpected behaviorsโespecially in failure scenarios. With a simple interceptor and repository, you can build idempotent POST APIs in Spring Boot easily.
Top comments (0)