Stripe uses a date as its version and it maintains state for each user, so it knows whether to serve an old version or latest and greatest. They offer UI and tools to manually migrate to a new version, with clear expectations and changelogs provided. They can build a rollout and sunset plan around that.
Protobuf is append only until you change the actual package. This gives you the opportunity to make additive changes without making a breaking change and you don't need to maintain separate releases as you make incremental updates or bug fixes. If the API changes significantly, it's no longer the same API, it's a new one.
> Even if you figured all of that out, links are not idiomatic if your users consume your api via an RPC or GraphQL.
Agreed, although you could model a link in the object-based structure representation to match the protocol, something like..
# RPC
{
"rel": "owner",
"procedure": "GetOwner",
"args": {
"id": 98765
}
}
# GraphQL
{
"rel": "owner",
"root": "owner",
"args": {
"id": 98765
}
}Why is this true for non-user facing urls? Seems like an argument could be made that you only need to support the urls for as long as the response was cacheable.
In fact, links in responses are probably easier to update, because waiting for client software to upgrade can take a long time - but waiting for old versions to fall out of caches or at least dwindle to a small enough number is likely a much shorter wait.
Furthermore, when your clients are composing links based on keys, there's no good way for the server to tell a client that the uri has changed - the composition is in the client, and the key (presumably) hasn't changed, so the thing the server would want to update is out of conventional reach.
But with uri's there's at least a conventional solution - redirects. That's not going to be an ideal solution; but as a way of limiting transition pain it might have a place.
All in all, I think links are likely less painful to use than keys when it comes to uri routing changes. (But really, try to avoid changing uri's in the first place, because whatever you do it's likely going to be painful somewhere).
You should be doing this. It helps a lot, actually.
“A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types“
In this case the only URL that is support long term is the entry point into the application. All other resources are discoverable from the index.
Consider your audience. Is it APIs like GitHub? Well guess what, you're stuck with maintaining backwards compat anyway, may as well have the relationships readable.
And if it's not and you control both ends then what does it matter because you can break links all you want.
It was touched on but less emphasized the relationships being expressed as links exposes further context than you otherwise get.
It's also nice to be able reason about shared domains. I.e. you might have the concept of a stock item and an order item. They are both represent the same thing in the real world and share a lot of information but depending on the value of self will tell you what kind of data you can expect to be a available and what domain you're in as well. Reading logs would be oh so much clearer.
(1) There can be several levels of entity identifiers, that is the same entity exists at several levels where it has different identifiers. Example: a computer has DNS name and IP address (and MAC address); a person may have several identifiers. These (initially independent) spaces can be structured differently:
(1.1) Layered structure like DNS and IP (1.2) Independent, e.g., a person has two passports from different countries
(2) Links (representing relationships) are attributes. Like all attributes they are functions which map input entity identifiers into output (address) space identifiers.
Taking this into account, the problem is that URLs frequently play two roles, that is, one address convention is used at two layers simultaneously which can lead to numerous problems and controversies:
o URLs are used at access (protocol) level where the goal is to identify computers and access paths within some service (and not entity). For example, this layer has no idea what this means: http://service.com/passports/1234 - it is used for HTTP access only.
o URLs are used to identify real entities and here we re-use the previous name convention for higher level purposes by identifying people like http://service.com/passports/1234 although we do not care about protocols or computer names.
In order to avoid serious problems we need to follow these steps:
* Recognize and document that there are two independent address spaces
* Define a mapping between these two spaces. Initially, it can be identity relationship, that is, every entity name (URL) is mapped to the same URL used for access.
* If necessary (and frequently highly desirable) use relative names by assuming that the higher level segments of the name can be restored from the context or configured separately.
These rules are especially important for extensibility and for complex evolving systems where the standards and conventions change in time.
You don't have to honor it forever; if you want to be good about planning for changes, you annotate each link with the latest date when it is guaranteed not to be expired, and the latest date an updated authoritative URL will be available (either as content in the resource representation or an HTTP redirect) if it expires on the earliest possible date. Consumers are then free to determine whether they need to update links or whether the data from which they come is stale (if your resources at any given link are immutable but for metadata about currency and links to updated versions if the content of the logical entity has changed, so that you can always recover the same version of an entity you fetched at the original URL so long as the URL hasn't expired, clients don't even need to check each references URL for updates, they can just check the base URLs of the entities they already have read that referenced them, and in the process update all referenced URLs; you kind of need that allow clients an approximately consistent view of data when they can't read it all simultaneously anyway, and if your API doesn't need that level of consistency, it then it certainly doesn't need link stability, either—you’ver already decided you are dealing with fundamentally ephemeral data,.so.if people don't follow links relatively quickly, they aren't getting meaningful data even if the links are permanently stable.)
If I were paying someone for access to such an API, I would do everything in my power to change providers. Tracking link expiration would be a terrible developer experience. Such a thing may be a sound design for a system, but it's not a good design.
Providers like Twilio and Stripe have been largely successful because they've aimed for making developer experience as non-sadistic as possible.
What's different about this proposal? It would seem to acknowledge and support that type of activity directly, rather than through ad-hoc and kludge type mechanisms that typically include paying somewhat shady 3rd-party services.
It seems like any of these schemes make assumptions that some things are invariant when switching to a new version, so you don't have to start over.
For example, if user IDs are just integers then you have a fairly conservative assumption that clients can store the user IDs and they can be reused with a new version. That is, users won't be renumbered. But everything else about the API can change between versions.
A site-relative URL also fixes part of the URL template between versions, so it can be safely stored. But the host can change.
I'm skeptical that URL's are all that helpful here. Why not do a POST using something like protobufs and avoid the issue entirely? An API like this is site-specific anyway and requires custom client code (other than the parsing layer which can be generated from a schema). It seems weird to pretend otherwise, as if some universal client existed. At best, you could use a browser for debugging, but that's about it.
With HATEOAS, at the best case is that clients just bump the entry point url (or even have it bumped for them) and then follow the links they are given. It's not possible to upgrade clients that way if they are constructing urls themselves.
Instead, you could just use a database of redirects which you append to when you make format changes. For some applications, the overhead of maintaining a redirect service is far lower than trying to maintain support for multiple formats. (And this redirect service may even be something you need/want for other reasons.)
I've found more success in supporting two formats using the v2 api to serve the v1 one.
/people/12345 becoming /people/v2/12345
Sure, you still have to support the old version (maybe only temporarily), but migrating schemas gets marginally easier (you can do it via an entirely different service, for example)
If a client gets a 410 when following a stored link, they can always find the new url for the resource by following links from the parent resource (this is recursive, all the way back to the root url).
My deeper point is that all this has been hashed out and settled by now. If you're still having this problem in 2019 you're either playing around or "Doing It Wrong".
> The server is now free to change the format of new URLs at any time without affecting clients (of course, the server must continue to honor all previously-issued URLs).
If you have to honor all previously-issued URLs then you aren't changing your format -- you are supporting two formats from now on, the old one and the new one.
You can of course tell your users that you will deprecate the old format, but unless you are as powerful as Google your users may prevent you from enforcing a deadline for deprecation.
If the URLs in your API responses are FQDNs rather than relative paths, all of this gets significantly harder to deal with.
Even if you figured all of that out, links are not idiomatic if your users consume your api via an RPC or GraphQL.