Malax
Malax

Reputation: 9604

Hypermedia API: How to document properly?

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? :)

Upvotes: 3

Views: 463

Answers (2)

Nicholas Shanks
Nicholas Shanks

Reputation: 10981

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.

Upvotes: 1

Robert Br&#228;utigam
Robert Br&#228;utigam

Reputation: 7744

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

Upvotes: 1

Related Questions