hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses API versioning and evolution with @hectoday/http

Why versioning

  • Breaking changes
  • The versioning contract
  • Project setup

Versioning strategies

  • URL path versioning
  • Header versioning
  • Query parameter versioning
  • Choosing a strategy

Building versioned APIs

  • Side-by-side versions
  • Version routers
  • Validation per version

Evolving without breaking

  • Additive changes
  • Deprecation
  • Field renaming and removal
  • Changing response shapes

Lifecycle management

  • Sunset policies
  • Monitoring version usage
  • Checklist

Deprecation

Warning before removing

Additive changes let you add features without breaking anyone. But sometimes you need to remove things. Maybe v1 needs to go away. Maybe a specific endpoint is being replaced. You can’t just delete it overnight. Clients depend on it.

Deprecation is the process of telling clients: “This still works today, but it’s going away. Start moving.” It gives them time to migrate before you actually remove anything.

The Deprecation header

RFC 8594 defines a standard HTTP header for this. It’s called Deprecation, and it tells clients that the endpoint or version still works but will be removed.

HTTP/1.1 200 OK
Deprecation: true
Link: <https://api.example.com/docs/migration>; rel="deprecation"

Deprecation: true means “this is deprecated.” The Link header points to the migration documentation so clients know where to go for instructions.

You can also specify when the deprecation started:

Deprecation: Sat, 01 Jan 2025 00:00:00 GMT

The Sunset header

RFC 8594 also defines Sunset. While Deprecation says “this is going away,” Sunset says “this will stop working on this specific date.”

HTTP/1.1 200 OK
Deprecation: true
Sunset: Sun, 01 Jun 2025 00:00:00 GMT

Together, these headers tell a clear story: “This still works today, but it will stop working on June 1, 2025. You have until then to migrate.”

Think of Deprecation as the warning and Sunset as the deadline.

Adding deprecation headers in Hectoday HTTP

The Version Routers lesson showed onResponse adding headers to every response. Here’s how to set up deprecation for the entire v1 API:

Code along
export const app = setup({
  onRequest: ({ request }) => {
    const url = new URL(request.url);
    const version = url.pathname.startsWith("/v2") ? "v2" : "v1";
    return { apiVersion: version };
  },
  onResponse: ({ response, locals }) => {
    response.headers.set("X-API-Version", locals.apiVersion ?? "v1");

    if (locals.apiVersion === "v1") {
      response.headers.set("Deprecation", "true");
      response.headers.set("Sunset", "Sun, 01 Jun 2025 00:00:00 GMT");
      response.headers.set(
        "Link",
        '<https://api.example.com/docs/v2-migration>; rel="deprecation"',
      );
    }

    return response;
  },
  routes: [...v1Routes, ...v2Routes],
});

Try it:

curl -i http://localhost:3000/v1/books

You should see Deprecation: true, Sunset, and Link in the response headers. Now try v2:

curl -i http://localhost:3000/v2/books

Clean headers. No deprecation. v2 responses are unaffected.

Deprecating individual endpoints

Sometimes you don’t want to deprecate the entire version. Maybe just one endpoint is being replaced:

route.get("/v2/books/popular", {
  resolve: () => {
    const books = getPopularBooks();
    return new Response(JSON.stringify(formatBooksV2(books)), {
      headers: {
        "Content-Type": "application/json",
        Deprecation: "true",
        Sunset: "Mon, 01 Sep 2025 00:00:00 GMT",
        Link: '<https://api.example.com/docs/use-top-instead>; rel="deprecation"',
      },
    });
  },
});

In this case, /v2/books/popular is deprecated, and clients should use /v2/books/top instead. The rest of v2 is perfectly fine and has no deprecation headers.

Deprecating individual fields

What about when you want to remove just one field from a response? For field-level deprecation, you include both the old and new field during the transition period:

// v2 response: author_name is deprecated, use author object
{
  "id": "book-1",
  "title": "Kindred",
  "author": { "id": "author-3", "name": "Octavia Butler" },
  "author_name": "Octavia Butler"  // DEPRECATED: use author.name
}

Both fields exist at the same time. Clients using author_name still work. New clients use author.name. After the sunset date, you remove author_name.

This is exactly how production APIs handle field-level changes. The next lesson covers the full four-step process for field renaming and removal.

Exercises

Exercise 1: Add Deprecation and Sunset headers to all v1 responses via onResponse. Verify they appear on v1 but not v2.

Exercise 2: Deprecate a single v2 endpoint (not the whole version). Add deprecation headers only to that endpoint.

Exercise 3: Include both old and new field names in a response (field-level deprecation). Document which is deprecated.

What is the difference between the Deprecation and Sunset headers?

← Additive changes Field renaming and removal →

© 2026 hectoday. All rights reserved.