Skip to content

Implement a full /v2 migration path with dual-serving and automated deprecation sunset for the Access API #278

Description

@Lakes41

Difficulty: Expert
Type: DevOps

Background
The README defines a strict versioning contract: /v1 routes stay backward-compatible, an x-guildpass-api-version header is returned on all responses, GET /health/live exposes server version, and deprecated routes serve a deprecation: true header with a minimum sunset period before removal — but no /v2 currently exists, and the sunset mechanism appears to be a policy statement rather than enforced tooling.

Problem
Without tooling to enforce it, the deprecation policy depends on manual discipline: nothing currently prevents a maintainer from removing a deprecated route before its sunset period ends, and there's no structured way to run /v1 and a future /v2 side-by-side with shared business logic.

Expected outcome
A /v2 route namespace exists alongside /v1 sharing the same underlying services (not duplicated logic), a machine-readable deprecation registry records each deprecated route/field with its deprecation date and minimum sunset window, and an automated check (runnable locally and intended for CI) fails if a route is removed before its recorded sunset date has passed.

Suggested implementation

  • Introduce a version-agnostic service layer (if not already cleanly separated from route handlers, per the README's "business logic lives in services" note) so /v1 and /v2 route handlers can both call the same underlying functions, differing only in request/response shape where needed.
  • Add a deprecation-registry.json (or similar) recording { route, deprecatedAt, minimumSunsetDays } entries, and update the deprecation: true header logic to read from this registry rather than being set ad hoc per route.
  • Add a script (npm run -w access-api deprecations:check) that fails if any registry entry's sunset window has expired and the route still exists, or conversely flags routes removed from code that are still referenced in the registry without being marked fully removed.
  • Pick one real, currently-awkward /v1 field or route to actually deprecate and migrate to /v2 as a proof of the pattern (e.g., a response field whose shape should change).
  • Update docs/openapi.json generation to emit separate specs (or a combined spec with version-tagged paths) for /v1 and /v2.
  • Document the full versioning/deprecation workflow for future contributors in the README's API Versioning section.

Acceptance criteria

  • /v2 namespace exists and shares business logic with /v1 (no significant duplicated service code)
  • Deprecation registry drives the deprecation: true header rather than manual per-route logic
  • A check script correctly fails when a route is removed before its sunset date
  • At least one real field/route is migrated end-to-end as a working example
  • OpenAPI generation covers both versions correctly

Likely affected files/directories

  • apps/access-api/src/routes/
  • apps/access-api/src/services/
  • apps/access-api/src/scripts/ (new deprecation check)
  • docs/openapi.json
  • README.md

Metadata

Metadata

Assignees

Labels

GrantFox OSSGrantFox Open Source Sponsorship program tagMaybe RewardedIssue may qualify for a reward upon successful completion per campaign rulesOfficial Campaign | FWC26Official FWC26 campaign issue — eligible for campaign scoring and rewardsdevopsInfrastructure, CI/CD pipelines, deployment automation, and toolingexpertExpert difficulty tasks requiring deep expertise and architectural decision-making

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions