3

I am in the middle of developing my first Hypermedia API. I thought I had a good grasp of things but when It comes to documenting the API, I start to question my understanding of the whole concept.

The core of the question comes down to documention, but It might be that I did not understand one or more aspects correctly. If so, please tell me :-)

Documenting Link Relations

Let's say I have a more or less generic link relation (https://example.com/rels/accounts) in my API to link related accounts. The exact meaning can change on context, right?

  1. On my billboard (or index), I might have a link with that relation to browse all accounts.
  2. On another resource, account group for example, it might just link to a specific subset of accounts belonging to that group.

How should I document that relation? Just saying that this is a link to a collection of accounts seems not enough. However, that is exactly what RFC5988 does, just describing what the link itself means. So that might be the way to go.

The problem gets worse with actual state transitions. Let's use https://example.com/rels/create-account as our example here. If the documentation just says "This is where you create a new account", It makes no statements on what I have to do with that link to create a resource. Where would the documentation be that states something along the lines:

You either POST multipart/form-data or application/json to this endpoint that contains at least the following fields [...]

The relation itself does not seem to be the right place. Especially when you consider that the payload to that URL might also change on context. In our example, having that relation on an account group would make it mandatory to omit the accountGroup field because the value is provided by the context.

Documenting Profiles

As far as I understood it, I can (and probably should) have a profile for each resource in my API, documenting the resource itself. This would include its properties and links that can occur. Would that be the place where I specify what the link means exactly?

Sticking to my earlier example, would I document for profile https://example.com/profiles/account-group that the link with the relation https://example.com/rels/accounts links to a collection of accounts that are part of this group?

That makes sense to me, but when we go into actual state transitions things seem to get messy.

State transitions

Let's say the client navigated to an account collection from an account group. The resource itself would not really differ from the resource that contains all global accounts. It would have pagination links and the links to the account resources themselves.

If that account-collection resource has a link with the relation type https://example.com/rel/create-account I would be in deep trouble, right? Because the information that this is an account-collection containing just accounts of a certain group is not encoded in the profile https://example.com/profiles/account-collection and can therefore not contain the information that clients have to omit the accountGroup property when posting to that resource.

Concrete Questions

  1. Am I right that relations definitions should be weak and not contain any information about how I can interact with the resource it links to?
  2. If so, can I expect from client to follow a link and then discover what they can do, based on the profile of that resource. This seems wrong, especially for state transitions.
  3. If profiles should document what a client might do with the linked resources, I cannot transport context across multiple "jumps" within the API, correct?
  4. Should I use even more profiles and relations? https://example.com/profiles/global-accounts and https://example.com/profiles/account-group-accounts come to mind.

The more I think about it, I must either miss a critical piece or it's something that can be solved in multiple ways. Because of that, I am aware that there might be no 100%-correct answer to this question. But maybe someone can enlighten me so that I can make my own trade-offs? :)

Community
  • 1
  • 1
Malax
  • 9,436
  • 9
  • 48
  • 64

2 Answers2

1

Let's take your points one at a time:

1. Link relation meaning based on context

To reformulate your question into code context: How do I document the "getAccounts()" method when it means something different in the class Billboard and AccountGroup?

The obvious answer is you don't document the method in general, but you document the classes and within them the methods. The RFC you're referring to tries to define relations that are in some sense generic, or should mean the same thing every time. You might re-use some of them, but you still have to document the method and what they mean in your class.

Class equals to your Media Type. So I propose you document your Media Type.

2. Links and Forms, how to define what to POST

If you document your Media-Types, you can define whatever you like in there, including how to use its links. However, I would recommend not defining the Media Type of the POST, but letting it up to Content-Negotiation.

The client knows it has to POST an Account, it will use some Media-Type it thinks is proper for this task. The server will then tell the client whether that format is acceptable or not, it can also give a list of acceptable Media Types in return.

3. Documenting Profiles

If by Profiles you mean Media-Types, then yes, you should document them. You should actually only document Media-Types.

4. State transitions, modifications to representations based on state

Since the account group is a resource anyway, the client should not actually supply it, it should be part of the "state" which group the new account belongs to.

In other words, the client gets a link, that already has the context of the current account group. The client has to post a generic account, but the server knows it should belong to the group in the current state (it is part of the URI for example)

So no, the client shouldn't know it has to omit some parameter.

5. Question

  1. Yes, relations should not define how to interact with the resource. Media Types could actually do that (like Forms defining it must be a POST, etc.), but more often than not it's not necessary.

  2. Yes, clients discover not only what transitions are available (links), but what methods are available. The methods (GET, POST, PUT) always mean the same thing, and they are not described in Media-Types, since Media-Types only describe representations, not resources. The server normally submits all the supported methods in a response, or explicitly in response to OPTIONS.

  3. I still don't really know what you mean by "Profile". If you mean relation profile, as in some data in the link definition, then no. The context/state travels in the URI. You can use the URI to "save" that the client is moving inside one account group for example.

  4. No, you shouldn't add semantics to link relations. The Media-Type adds the semantics to links.

HTH

Robert Bräutigam
  • 7,514
  • 1
  • 20
  • 38
  • A profile defines additional constraints to an existing media type. `application/hal+json; profile=https://example.com/profile/customer` would specify that the document adheres to the the hal+json media type but also has in constrained by the given profile. In a nutshell, you do not really define new media types, but rather augment them with your own rules. It's a pretty neat concept IMO. Further info: https://tools.ietf.org/html/draft-kelly-json-hal-08#section-7.1 https://tools.ietf.org/html/rfc6906 – Malax Jan 16 '17 at 16:02
  • Regarding 2: But I still have to document what business specific data the endpoint expects, regardless of its encoding. This becomes even more problematic with `PATCH` requests which defiantly need some documentation. Would this be part of the documented media-type? I.e.: `application/vnd.acme.customer` expects that you post X, Y but not Z to the `https://example.com/rels/edit` link? – Malax Jan 16 '17 at 16:17
  • Thanks for the links about Profiles, seems to me like some sort of 'subclasses' for Media-Types. I have to think about that some more. Regarding 2: The link defines where you can navigate. This should be GET. When you are there at a resource, edit is always PUT. But in general, a link should not define what Media-Type might follow. That is content-negotiation, part of the GET request. Does that make sense? – Robert Bräutigam Jan 16 '17 at 16:47
  • I'm on board with content-negotiation. But that links should always be GET confuses me TBH. I would use links to indicate what actions can be performed on the current state of the resource. Take `delete` for example. Why should I follow the link with a GET? Or would the fact that I can delete the current resource be solely handled by an `OPTIONS` request to the current resource (or `Allow` header)? But I have still problems with the documentation required for the resource itself (not its representation). I have no "key" so-to-speak where I could attach that documentation to. – Malax Jan 16 '17 at 17:20
  • To clarify the comment above: The resource might have `PATCH` and `DELETE` in its `OPTIONS` response. But what payload is required needs to be documented, right? Even if I define a media-type for that (i.e.: `vnd.acme.user-patch`) that specifies how a user patch looks like, I still need to document that a specific resource like `user` supports that media type as its `PATCH` payload (or maybe more). But if I do that, i would need to document to which resource type my delete link points to because i should not identify the resource by its URL. It's a bit of 'Chicken or the egg' problem. – Malax Jan 16 '17 at 17:27
  • Links that are not GET could be called a Form (using HTML terminology). Forms are perfectly valid things to include in representations, though I find they are very rarely needed. They are *not* needed for DELETE and PUT etc, since the semantic of those is clear. You can't (shouldn't) define the payload format at all! Indeed the client might supply you with an Account message you don't understand! In which case you just say 415. – Robert Bräutigam Jan 16 '17 at 17:31
  • The client may decide to send you https://tools.ietf.org/html/rfc6902 in a PATCH. It's the client's choice! You as the server might support standardized PATCH formats, or you can say you refuse (415) and list some accepted ones. At the end the client may know one of your supported formats or not, but the format should not be "fixed" with the resource. You might however list Media-Types the client has to support in order to consume your resources. Hence document your Media Types. – Robert Bräutigam Jan 16 '17 at 17:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133291/discussion-between-malax-and-robert-brautigam). – Malax Jan 16 '17 at 17:35
1

I will try to answer more succinctly than Robert's answer, which I think can get confusing.

Let's say I have a more or less generic link relation (https://example.com/rels/accounts) in my API to link related accounts. The exact meaning can change on context, right?

No. The meaning of a link relation is static. Your "list of accounts" relation on your index (list of lists) page is specific to your application. In your other example, account group, you are already in a "collection" resource, and members of that collection have link relation "item" (see the list of IANA link relations).

The problem gets worse with actual state transitions. Let's use https://example.com/rels/create-account as our example here. If the documentation just says "This is where you create a new account"

I would instead add a "create-form" link (another standard IANA link relation) to the "list of accounts" page. Your clients would then go: start -> list-of-Xes -> create-form -> submit. There would be no "create-an-X" or "create-a-Y" link relations. You probably would not allow clients to create new collection types either. This makes navigating the API lengthier for clients but reduces the amount of stuff they need to know (API breadth).

Would [a profile] be the place where I specify what the link means exactly?

If you make your link relations generic except for one to describe each model class, you will not have to document these in profiles on a per-model-class basis.

If that account-collection resource has a link with the relation type https://example.com/rel/create-account I would be in deep trouble, right?

Yes, so don't do that! What you are describing there is linking an existing resource to a collection (by IANA's definition). What I would advise is that you support clients being able to do this:

LINK http://site/account-collections/some-collection HTTP/1.1
Link: <http://site/accounts/some-account-id>; rel=item
Authorization: Token abc123

This simple (complete!) HTTP request has the semantics of adding an "item" link containing the existing account's URI to the collection you wish to add it to. It does not create a new account. The LINK method is still in draft but has existed in some form since HTTP 1.1 (1997).

Robert wrote:

You should actually only document Media-Types.

I disagree. You should document link relations too, but try not to create them if you can use an generic one.

Community
  • 1
  • 1
Nicholas Shanks
  • 10,623
  • 4
  • 56
  • 80