11 August, 20243 minute read

Using the Sunset header with GraphQL

Try as we might to design APIs that last, it is very difficult to completely avoid the need for breaking changes. The Sunset HTTP header offers a standardized way to inform clients about the deprecation of API endpoints in a programmatically interpretable way. In my previous post on this topic I discussed what this header is and why it’s important for managing API lifecycles—but didn’t explain how to use it.

I’ve since published a package called graphql-sunset which lets you sunset parts of your schema with no code changes. Let’s take a look at how it can be used.

Integrating graphql-sunset with Apollo Server

Install the plugin

Using your favorite JavaScript package manager, install the graphql-sunset package:

Click to copy
$ pnpm add graphql-sunset

Then, import the Apollo server plugin and add it to your server’s plugins array:

Click to copy
import { ApolloSunsetPlugin } from 'graphql-sunset'; // ... const server = new ApolloServer({  // ...  plugins: [    new ApolloSunsetPlugin(),  ],});

Define the @sunset directive

Fields are marked for sunset by applying a @sunset directive. Because it’s possible to follow either a code-first or schema-first approach to defining your schema, the plugin itself doesn’t define this for you. Out of the box, the plugin expects the directive to take two arguments:

  1. url: This will be used to fill in the Link header.
  2. when: This should be an RFC3339 date time string. It will be used to populate the Sunset header itself.

The ApolloSunsetPlugin constructor takes an options bag which can be used to tweak these defaults. Assuming you are happy with these, however, you’ll want to add the following to your schema:

Click to copy
directive @sunset(  url: String!  when: String!) on ARGUMENT_DEFINITION | FIELD_DEFINITION | INPUT_FIELD_DEFINITION

Mark fields for sunsetting

WIth this in place, you can now go ahead and start marking fields in your schema for upcoming removal.

Let’s say that we currently have a getUser query field that we want to remove in favor of user, following my GraphQL naming conventions. We’ll remove the old field at the start of October 2024, by annotating the query field with a @sunset directive like so:

Click to copy
type Query {  getUser(id: ID!): User @sunset(    url: "https://docs.sophias-api.com/notices/removing-getuser"    when: "2024-10-01T00:00:00.000Z"  )  user(id: ID!): User}

Test it

Executing a GraphQL operation which selects the getUser query field will cause us to receive Sunset and Link headers, just like we expect to:

$ curl --include --request POST \    --header 'content-type: application/json' \    --url http://localhost:3000/graphql \    --data '{"query":"query { getUser(id: \"user_abc\") { id } }"}'HTTP/1.1 200 OK...sunset: Tue, 01 Oct 2024 00:00:00 GMTlink: <https://docs.sophias-api.com/notices/removing-getuser>; rel="sunset"...

Notes

If the consumer were to request multiple fields which are being sunset, then the Sunset header will be set to the earliest sunset date. The Link header will include links to all the associated sunset URLs. This is handy, because it lets consumers track metrics such as the average number of deprecated resources they are requesting per GraphQL operation.

If the server already had a Link header set, then this package will preserve the existing Link header value(s) and merge in the sunset links, ensuring that no information is lost.

Finally, it’s important to note that graphql-sunset is a purely informational tool. It doesn’t handle the dirty work of actually removing the deprecated functionality from your API at the time of sunsetting. That responsibility still lies with you, the API designer.

Use cases

The main use case here is to employ something like WeWork’s faraday-sunset library to automatically alert on the client side about impending breaking changes. Getting consumers to migrate their code in time is really hard, especially in a context where your consumers are external to your organization. Even prior to thinking about actioning any code changes, it can be hard to just communicate the breaking change.

Because the Sunset header is programmatically interpretable, it’s possible to build really nice automated workflows around it. An upcoming version of Rye’s SDK will likely start doing this, because managing breaking changes has been a recurring problem for us as we build out new functionality and run up against the limits of our abstractions.

There are other ways you can achieve this, but the great thing about the Sunset header is that it’s standardized. There might not be a lot of tooling around it today, but I expect that to quickly change. One of the things I really love about GraphQL compared to RESTful API architectures is that GraphQL has a rigorously defined spec that helps homogenize API behavior.

On the other hand, every REST service feels like a snowflake because there are very few guardrails in place to prevent API designers from doing whatever they like. Standardization is a good thing that makes developers more productive—we need more of it.

Conclusion

Integrating the Sunset header into your Apollo GraphQL server using graphql-sunset is a proactive step toward better API management. By providing clear deprecation notices, you can improve the developer experience and ensure a smoother transition for your API clients.

For more details, check out the graphql-sunset GitHub repository.

Feel free to reach out with any questions or feedback!

Don't want to miss out on new posts?

Join 100+ fellow engineers who subscribe for software insights, technical deep-dives, and valuable advice.

Get in touch 👋

If you're working on an innovative web or AI software product, then I'd love to hear about it. If we both see value in working together, we can move forward. And if not—we both had a nice chat and have a new connection.
Send me an email at hello@sophiabits.com