Skip to content

Deprecate legacy REST API endpoints with Sunset headers and 410 Gone responses#1554

Closed
jrhoads wants to merge 9 commits into
masterfrom
retire-legecy-endpoints
Closed

Deprecate legacy REST API endpoints with Sunset headers and 410 Gone responses#1554
jrhoads wants to merge 9 commits into
masterfrom
retire-legecy-endpoints

Conversation

@jrhoads

@jrhoads jrhoads commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Purpose

Introduce a deprecation mechanism for legacy REST API endpoints (/data-centers, /members, and /works) by communicating their upcoming sunset date via standard HTTP headers and, after the sunset date, returning 410 Gone responses.

Approach

A reusable LegacyRestDeprecation controller concern was created and included in the three legacy controllers. It allows each controller to declare a sunset date, a documentation link, and a replacement endpoint. Before the sunset date, responses include Sunset and Link headers per RFC 8594. After the sunset date, GET requests to index and show actions are rejected with 410 Gone. RecordNotFound errors are still rendered as 404 Not Found but also include the deprecation headers.

Key Modifications

  • Added app/controllers/concerns/legacy_rest_deprecation.rb with configurable class methods, before/after action hooks, and custom error rendering.
  • Included the concern in:
    • DataCentersController (replacement: /clients)
    • MembersController (replacement: /providers)
    • WorksController (replacement: /dois)
  • Set the shared sunset date to Time.utc(2026, 7, 1).
  • Added/updated request specs in spec/requests/data_centers_spec.rb, spec/requests/members_spec.rb, and spec/requests/works_spec.rb to assert header behavior before sunset and 410 behavior after sunset.
  • Added expect_legacy_sunset_headers and expect_no_legacy_sunset_header helpers to spec/support/request_helper.rb.
  • Updated crass from 1.0.6 to 1.0.7 in Gemfile.lock.

Important Technical Details

  • The concern hooks only index and show actions.
  • The Sunset header value is generated with .httpdate to conform to HTTP-date formatting.
  • The Link header uses rel="sunset" pointing to the configured documentation URL.
  • After sunset, the response body is JSON:API style with "status": "410" and, when a replacement path is configured, a detail message suggesting the replacement endpoint.
  • legacy_render_not_found overrides the default RecordNotFound handler so that 404s on legacy endpoints still communicate deprecation information before sunset.
  • Replacement endpoints (/clients, /providers, /dois) are unaffected.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Reviewer, please remember our guidelines:

  • Be humble in the language and feedback you give, ask don't tell.
  • Consider using positive language as opposed to neutral when offering feedback. This is to avoid the negative bias that can occur with neutral language appearing negative.
  • Offer suggestions on how to improve code e.g. simplification or expanding clarity.
  • Ensure you give reasons for the changes you are proposing.

@jrhoads jrhoads requested review from a team, codycooperross and richardhallett June 29, 2026 14:58
@richardhallett

Copy link
Copy Markdown
Contributor

The idea of sun setting and disabling are not the same, which I feel like the description tries to say but the code seems to only do the sunset when its all disabled, just with the error message changing if a replacement URL is given.

I like the sunset header (new one for me) but this should be active now before we disable the routes, as reading the RFC it's meant to give time for people to transition to the correct routes.

In addition when we're ready to actually disable the route, I probably would do this at the load balancer and just return either a redirect or the 410 (a custom json can be included if desired). This would prevent it hitting the application, so reduces load.

So maybe this PR could just be to enable sunsetting? which might be as simple as just adding a sunset concern which adds the header to the suggested replacement and I'd have this active all the time, we just include it on endpoints we wish.

@jrhoads jrhoads changed the title Add opt-in DISABLE_LEGACY_REST flag to sunset legacy v1 REST endpoints with 410 Gone Deprecate legacy REST API endpoints with Sunset headers and 410 Gone responses Jun 29, 2026
@jrhoads

jrhoads commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

The idea of sun setting and disabling are not the same, which I feel like the description tries to say but the code seems to only do the sunset when its all disabled, just with the error message changing if a replacement URL is given.

I like the sunset header (new one for me) but this should be active now before we disable the routes, as reading the RFC it's meant to give time for people to transition to the correct routes.

In addition when we're ready to actually disable the route, I probably would do this at the load balancer and just return either a redirect or the 410 (a custom json can be included if desired). This would prevent it hitting the application, so reduces load.

So maybe this PR could just be to enable sunsetting? which might be as simple as just adding a sunset concern which adds the header to the suggested replacement and I'd have this active all the time, we just include it on endpoints we wish.

@richardhallett You are correct, that was not how the SUNSET header was intended. I've updated the PR in a couple of key ways. It has an explicit date for to sunset and adds the sunset header any time before that date. After that date the path will respond with a 410 gone response.

I agree that the Load balancer is the correct place for this behavior ultimately in order to have less load on the application. I'd like to propose a YES-AND approach here where the application will handle it up-to and until we go ahead and have the load balancer take over. After that time we can remove the controller logic all together.

I've also updated the PR description and title to reflect this new approach.

@jrhoads

jrhoads commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

I'm going to go ahead and close this. I've implemented the change at the load balancer level. And July 1 is tomorrow. There's not a reason to use the sunset header now. Perhaps in the future we could use it, but it's a bit later now.

@jrhoads jrhoads closed this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants