Hacker News new | ask | show | jobs
by AffableSpatula 4924 days ago
I wrote the spec for application/hal+json that got compared to WS-star , here's where I'm coming from:

JSON doesn’t have links. Establishing some basic conventions for that makes complete sense. Defining those conventions is called a spec. Giving a payload that follows conventions a name also makes sense. Establishing that is called registering a media type identifier.

It makes no sense to keep reinventing the linking wheel in every API . Pretending like a very minimal media type like hal+json is akin to WS-* is incredibly disingenuous and/or stupid.

Establishing a standard media type like hal+json with a bunch of conventions allows us to build generic tooling that can help with both serving and consuming payloads that contain links.

Being pragmatic is great, but misrepresenting a genuine effort to improve the status quo and improve the API ecosystem in a reasonable, non-complicated fashion as ‘hand-waving’ or 'unnecessary' is not very constructive.

3 comments

Are there any examples of what, for example, the Flickr or Twitter API would look like under hal+json?

I'm struggling to see how application/hal+json would help me write a client to upload a photo. I imagine that with or without hal I'm ultimately going to POST the data to some endpoint. The question is, how do I figure out what endpoint to use?

Without hal+json I need to read the API docs to discover that "/photos/" is the correct endpoint to post to. But with hal, it seems I still need to read the docs to discover that within the hypermedia file, the key "photos" holds the value for the URI template. So either way, I need to read the docs, and either way, my client breaks if the meaning of the string "photos" changes.

You still need to know the meaning of each link relation, that is true. When hypermedia advocates gloss over this, or imply that clients will just magically know what to do with all the data and links coming from the API entry point, it annoys me. It's dishonest.

But what you gain from link relations is worth advocating for:

* Link relations can be standardized across APIs. This opens the door for clients to infer functionality when presented with links it recognizes. There is a list of currently standardized link rels here: http://www.iana.org/assignments/link-relations/link-relation...

* Link relations provide an abstraction layer over the implementation, which may change. They're not unlike an API in and of themselves. As long as the 'photos' link relation does not change, it can point to whatever URL it wants, and that URL or URL structure can change over time without damage to the client.

* Using exclusively URLs to identify resources, as opposed to making the client memorize how to take any ID and map it into a URL, frees up an API to refer to resources outside its scope. That is big, enabling multiple APIs to connect as a proper ecosystem, with outgoing and incoming links. We can't do that today.

> As long as the 'photos' link relation does not change, it can point to whatever URL it wants, and that URL or URL structure can change over time without damage to the client.

So here's where my skepticism kicks in:

How often are you really expecting your URL structure to change, in practice? And when the URL structure does change, what percentage of the time does the change in URL structure also coincide with a change in semantics that clients logic will need to be changed to take into account?

In my experience the answer to those questions is 1) Very rarely. 2) Nearly always.

You're preaching for YAGNI, which is a philosophy that's more good than bad, for sure.

I am lamenting that doing hypermedia APIs properly isn't the default in Rails. I call out Rails because its massive appeal (to which I am no stranger) is doing It right by default, all over the place for the common case.

That obviates YAGNI, because it implies no extra cost incurred for doing it right. I'd rather enable It rather than assume I ain't gonna need It, all things being equal.

That said, I have worked in companies large enough that the interlocking pieces are pretty far removed, yet expected to interoperate fully. Having and using link relations could have saved us a couple of headaches. I am hopeful the Rails-api project might approach this with more.... maturity?

> That obviates YAGNI, because it implies no extra cost incurred for doing it right. I'd rather enable It rather than assume I ain't gonna need It, all things being equal.

All I can say to this is that we have vastly different approaches to engineering.

Features are debt. Adding features you don't need now and will probably never need in service of some kind of ideological purity ("doing it right") is the worst kind of deficit spending.

I've done my time in big enterprise vast complex interlocking highly abstracted development. For every 1 time prematurely optimizing for flexibility happened to pay off, it resulted in onerous development and maintenance overhead for no benefit 9 other times. Do the simplest thing that works. If you don't need it now, assume you ain't gonna need it and only build it if and when you have a pressing need for it. To me, these are the very core of sound, reliable engineering.

Your applicaiton's link relations should be URLs. This means that you can expose each bit of documentation for each rel at their URL. So every time you see a link in a hal+json document, you can follow the _rel's_ URL, and fetch the documentation for it.. that turns out to make the API very easily discoverable, and gives you a way to model/manage your API's documentation in a consistent way.

You can actually see what that looks like in practice here (click one of the book icons on one of the links on the left):

http://haltalk.herokuapp.com

That's a really nice example of it in action. Thanks for putting in the effort to explore standardising links like this.

I really like the human discoverability feature that you've demoed, and gameche's point about being able to include resources outside the scope of an API is certainly interesting, but I'm still confused about the primary feature you championed, which is the ability to easily change URL structures.

You championed that as a core feature in your post: "make it painless to change your application’s URL structures further down the line".

dhh's retort knocked that feature on it's head, didn't it? Once you settle on coding against a particular section of an API, you're relying on a URL. Your feature works with an API depth of 1, but beyond that what is the proposed approach? To traverse the API from "/" every time, making multiple calls until you discover the right link reference which contains the latest URL for that resource?

Do you mean like http://haltalk.herokuapp.com/rels/signup? But every site will have different requirements or mandatory fields on signup, so there's still no "discoverable" way to sign up to Amazon and eBay and Google without a human looking at http://amazon.com/rels/signup and http://ebay.com/rels/signup, etc., in which case you're back to still needing to read the documentation.

I guess it's a slight improvement to know exactly where the documentation for a particular link relation will live instead of having to search for it, but it doesn't seem to solve an especially difficult problem.

it is an example of a discoverable API with discoverable documentation though, which is what you were asking for I think.. maybe not?
Ah, maybe I misunderstand what you're trying to do. I thought Hal was trying to make it possible to write clients that can buy from Amazon or eBay without knowing anything about the specifics of either. But actually what it's attempting is a sort of machine-assisted documentation system? (Since you still need a human to read the docs to find out how to signup, place orders, and so on.) I can see how that might work, but it doesn't seem like an important problem to solve.
That is one of the benefits that relates to documentation, which is the part of your comment I was responding too.

The larger goal of hal+json is to establish some conventions for linking that allow the development of generic tools for doing hypermedia. Not to create magical machine clients that can interact with any random API you point them at. Nobody made that argument so I don't know why DHH addressed it in his post. I'm guessing he ran out of things to be an angry-pragmatist about.

Hypermedia doesn't claim that you don't have to read the docs, that's a misconception. You do have to read and know how to parse the file formats.

The main advantage is that know that your application supports Twitter, it also supports every other service that uses the same file formats, since they can be shared, re-used and standardized. Obviously, two services can't use the same URLs, so if you hardcode them, you're locked in.

I can imagine Twitter publishing a application/tweet+json media type or similar, and clients and servers either supporting this or not (and exchanging Accept headers and 415 Unsupported Media Types as they go), but I don't get what that has to do with HAL.

For example, the "comments" link relation in

http://developer.github.com/v3/pulls/

points to

https://api.github.com/octocat/Hello-World/issues/1/comments

but no-where in the document does it say what media types that endpoint will accept.

JSON and other non-natively-hyperlinked-media (e.g PNG, MP3, etc) can use the Link header to add hyperlinks. It's HTTP that is hypermedia aware, not the content-type, so you dont have to go out and invent image/png+hal.

TL;DR You dont need to create a new media-type to make JSON hypermedia. All requests are hypermedia by virtue of the fact that it uses HTTP as the transport protocol.

> It's HTTP that is hypermedia aware, not the content-type

1. That's highly debatable, the very content type from which REST was extracted is hypermedia-aware

2. The LINK header only works for very shallow and broad linking, making it contextual to the content type will have as high a complexity (if not higher) as codifying hypermedia in the content type (consider a resource listing other resources, the equivalent to the HN frontpage, how are you going to match a given entry in the media — which may have a number of inline metadata — to its LINK? anchor? now you need to define an anchoring scheme in your content type. A link-extension? Now you need to define that, and you need to define the relation between the link extension and the content type. And of course you also need a custom relationship, which you'll also have to define, and you need your clients to correctly handle upwards to hundreds of LINK in a single resource) (and even with that, you're also making the client more complex because he'll need an explicit link-resolution step... and then, are URI templates allowed at all in a LINK?)

"JSON doesn’t have links."

JSON doesn't, but HTTP does. Why not use Link headers?

Link header parsers are far less ubiquitous than json, and Link headers aren't very good for use cases like representing links that come from items in a collection, there are also issues relating to the maximum feasible size for HTTP headers and/or the header block as a whole.

Link relations are useful for adding links to media types that can't support links (e.g. images, etc), and for layering protocols (e.g. Linked Cache Invalidation), but for normal APIs it makes your clients life much easier if you just put them in the body.

Would this be a better fit as part of an OPTIONS request?

OPTIONS /orders

  {
    "GET": {
      "description": "All the orders."
      "links": {
        "self": { "href": "/orders" },
        "next": { "href": "/orders?page=2" },
        "find": { "href": "/orders{?id}", "templated": true },
        "admin": [
          { "href": "/admins/2", "title": "Fred" },
          { "href": "/admins/5", "title": "Kate" }
        ]
      }
    }
  }
That's another option, yes.