Skip to content

Instantly share code, notes, and snippets.

@iamvery
Last active November 15, 2019 07:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamvery/c65ae9eb2a88a52f21a1 to your computer and use it in GitHub Desktop.
Save iamvery/c65ae9eb2a88a52f21a1 to your computer and use it in GitHub Desktop.
Proposal for JSON API deprecation support

Proposal for http://jsonapi.org/ Deprecations

Purpose

One of the benefits of a hypermedia API is the ability to make changes to the response without breaking existing clients. Some types of change are handled for easily than others, e.g. renaming a resource and returning 301 Moved Permanently. However, if you were to rename an attribute or relationship your clients would not know where to find the new names. A common solution to this problem to adopt a policy of only adding attributes and relationships. Such a policy would prevent clients of loosing track of these values, but it comes at a price:

  1. more noise in your responses
  2. more bits in your responses
  3. more things to maintain as the interface continues to grow and change

After a number of changes, your resource might start to look like this:

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "designation": "How to even: You literally can't",
      "epithet": "How to even: You literally can't",
      "moniker": "How to even: You literally can't",
      "name": "How to even: You literally can't",
      "title": "How to even: You literally can't"
    }
  }
}

You shouldn't be stuck with this forever. In fact, it's entirely plausible that client's could be weaned off the "old" forms and eventually remove them. The trick is communicating this to them.

Use Cases

Renaming an attribute

Similar to the above example, you have created a resource using some name for an attribute that you wish to change. Perhaps this change is justified by some additional context you have gained in your domain. It would improve the expressiveness of your service.

Renaming a relationship

Essentially the same reasoning as renaming an attribute.

Approaches

Deprecation Object

{
  "resource": "articles",
  "type": "attribute",
  "name": "epithet",
  "replacement": "title",
  "drop-dead-date": "2015-12-31"
}

Deprecation Top-Level Member

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "epithet": "How to even: You literally can't",
      "title": "How to even: You literally can't"
    }
  },
  "deprecations": [
    {
      "resource": "articles",
      "type": "attribute",
      "name": "epithet",
      "replacement": "title",
      "drop-dead-date": "2015-12-31"
    }
  ]
}

It would be up to the client to honor these deprecations. Perhaps a recommendation would be made that client's output deprecation noticed to the logs for visibility.

Questions?

  • I'm not sure if type is needed? Can a resource have an attribute and relationship by the same name?
  • I'm really not set on any of the terminology or design. Maybe there are better words for all the things?
  • Are there any other cases where deprecation would be needed? The only other thing I could think of that's different from service-to-service is meta, but I sort of feel like that's intentionally fluid. Not really worth adding any structure to.
@hibaymj
Copy link

hibaymj commented Jul 8, 2017

I like the thought, as I think this is important for the management of long running services. However, like the URL patterning in json-api I wonder if you have thought about this as a protocol or vocabulary concern, not one to be solved within json-api.

The sunset header from Erik Wilde would be a protocol solution for this, and something like a dynamic ALPS profile might be better served to broadcast semantic changes.

@ekampp
Copy link

ekampp commented Nov 15, 2019

In our organization, we solve it like this:

{
  data: { ... },
  meta: {
    deprecations: {
      attribute_name: {
        replacement: "json.path.to.0.replacement", // or null if no replacement
        removal_date: "2020-01-31"
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment