u/Entire-Position9690

We built a Spring Boot starter that automates API deprecation with RFC-compliant headers, 410 Gone enforcement, and metrics

If you need to retire an old API version, but you have no idea which clients are still hitting it, no standard way to warn them, and no clean mechanism to eventually shut it off.

We built spring-api-sunset to solve this. It's a lightweight Spring Boot 3.x starter that manages the full API deprecation lifecycle through a single annotation.

What it does

Add @Sunset to a controller or method:

@RestController
@RequestMapping("/api/v1/users")
@Sunset(date = "2025-10-01", since = "2025-04-01", replacement = "/api/v2/users")
public class UserControllerV1 {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}

From that point on, the library automatically:

  • Adds RFC 8594 Sunset and RFC 9745 Deprecation headers to every response, so clients can programmatically detect the deprecation
  • Adds a Link header pointing to the replacement endpoint
  • Returns 410 Gone with an RFC 7807 Problem Detail body once the sunset date passes (configurable — you can also choose to keep serving with warning headers)
  • Records Micrometer metrics (api.sunset.requests counter with endpoint/consumer tags, plus gauges for deprecated and sunset endpoint counts)
  • Exposes an actuator endpoint at /actuator/api-lifecycle that lists all deprecated endpoints with their dates, replacement URLs, days remaining, and current phase

The problem this solves

Most teams handle API deprecation ad-hoc: a comment in Slack, maybe a custom header someone remembers to add, and a TODO that never gets done. Meanwhile clients keep hitting the old endpoint because there's no machine-readable signal telling them it's going away.

The HTTP standards for this exist (RFC 8594 for Sunset header, RFC 9745 for Deprecation header) but nobody implements them because it's tedious boilerplate. This library makes it a one-liner.

The consumer tracking feature is particularly useful — set spring.api-sunset.consumer-header=X-Api-Key and your metrics will show exactly which API consumers are still on the deprecated version, so you can reach out to them directly instead of broadcasting to everyone.

Configuration

spring:
  api-sunset:
    enabled: true
    on-sunset: return-410    # or "continue" to keep serving with headers
    consumer-header: X-Api-Key

What the client sees

Before sunset — normal response with warning headers:

Sunset: Wed, 01 Oct 2025 00:00:00 GMT
Deprecation: @1743465600
Link: </api/v2/users>; rel="successor-version"

After sunset — 410 Gone with Problem Detail:

{
    "type": "about:blank",
    "title": "Gone",
    "status": 410,
    "detail": "This API endpoint was sunset on 2025-10-01. Use /api/v2/users instead.",
    "instance": "/api/v1/users/123"
}

Details

  • Java 21+, Spring Boot 3.4+
  • Micrometer and Actuator are optional dependencies — metrics and the lifecycle endpoint only activate if they're on the classpath
  • Method-level @Sunset overrides class-level
  • Zero boilerplate — just add the dependency and annotate

Available on Maven Central:

implementation("io.github.atlancia-labs:spring-api-sunset:0.1.1")

GitHub: https://github.com/Atlancia-Labs/spring-api-sunset

Would love feedback, especially if you've dealt with API deprecation at scale and see gaps in this approach. What would you add?

reddit.com
u/Entire-Position9690 — 8 days ago

Spring Idempotency Kit is a production-ready library that ensures methods execute exactly once per unique key — preventing duplicate operations in distributed systems.

Problem it solves: Retries, timeouts, and concurrent requests cause duplicate executions. Double charges, repeated orders, webhook duplication. This library eliminates that.

All you do:

@Idempotent(headerName = "Idempotency-Key")
public PaymentResponse processPayment(PaymentRequest request) {
    // Same key = same result, every time
}

The library handles:

  • Redis-backed caching — first request executes, retries return cached result
  • Distributed locking — concurrent duplicates are rejected or wait for the original to finish
  • Failure strategies — fail-open (keep running) or fail-closed (503 if Redis is down)
  • Built-in metrics — cache hits, conflicts, execution timing
  • Zero boilerplate — Spring Boot auto-configuration, just annotate

How It Works

  1. Client sends a unique key (header or derived from request)
  2. Cache hit → return immediately
  3. Cache miss → acquire distributed lock, execute, store result
  4. Retry with same key → returns cached result

Requirements

  • Java 21+
  • Spring Boot 3.4+
  • Redis

Links


Built by Atlancia Labs

u/Entire-Position9690 — 17 days ago