Skip to content

Instantly share code, notes, and snippets.

@technoweenie
Created October 3, 2012 11:21
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save technoweenie/1b6ba8757e8166d1f0ce to your computer and use it in GitHub Desktop.
Save technoweenie/1b6ba8757e8166d1f0ce to your computer and use it in GitHub Desktop.
Hypermedia question

People have been talking about including hypermedia with REST Resources, but there are very few public examples that implement all of it. One common technique is to add *_url attributes:

{ "id": 1
, "self_url": "/issues/1"
, "comments_url": "/issues/1/comments"
}

This only gives you room to put a URL. Because of this, I've been leaning towards HAL:

{ "id": 1
, "_links":
  { "self": {"href": "/issues/1"}
  , "comments": {"href": "/issues/1/comments"}
  , "close": {"href": "/issues/1/close", "method": "post"}
  }
}

Should hypermedia tell you when you have permissions to access those resources?

// admin permissions
{ "id": 1
, "_links":
  { "self": {"href": "/issues/1", "method": "get,patch,delete"}
  , "close": {"href": "/issues/1/close", "method": "post"}
  }
}

// read-only permissions, no access to close/update issues
{ "id": 1
, "_links":
  { "self": {"href": "/issues/1", "method": "get"}
  }
}

The HAL spec doesn't mention method properties at all. But, I think letting a client know the difference between an Issue I can edit or close is very useful. Is it any better if I have a separate relation for each action?

{ "id": 1
, "_links":
  { "self": {"href": "/issues/1", "method": "get"}
    "edit": {"href": "/issues/1", "method": "patch" }
    "delete": {"href": "/issues/1", "method": "delete" }
  , "close": {"href": "/issues/1/close", "method": "post"}
  }
}

Finally, is the "close" relation even appropriate? Or should I assume clients know they can close Issues by setting "state" to "closed" (which is how the GitHub Issues API works)?

@andreineculau
Copy link

I read through the comments, and I didn't see anyone touch on the freshness of the permission to use method M (see CORS). Sorry if I missed it.

Oct 3 - I make a request and get back information that I can edit resource R
Oct 4 (after some time window, or after many other requests that changed the state of resources) - I try to edit R and fail

If you do the same, but use OPTIONS instead:
Oct 3 - same, just that you only get a link to the resource R - what you can do with it remains unknown
Oct 4 - OPTIONS R gives you back GET (no edit), and thus you cannot edit R, but you can read R

I personally like the granularity that OPTIONS gives you. I can refresh the permission for a resource R1, without refreshing the representation of another resource R2 that links to resource R1.

@LouisStAmour
Copy link

I agree with @andreineculau, though I'm not as convinced that OPTIONS is a necessity (it's an option, har har)

I think this needs to be handled using a smarter client. After all, given REST's preference for stateless server communication, you'd want to have a background poll ask for OPTIONS and update the display or perform another action (save a draft?) before a user clicks a button and tries to edit. I'm reminded of the traffic-less session sharing specified in OpenID Connect, where you can timeout a session on another domain in another tab by having support for smarter clients polling cross-domain cookies.

That all said, what's wrong with trying and failing to Edit, if a client is graceful enough to recover from errors without data/intention loss? Or, it's a pain, but if permissions are given as part of a session object, then cached as a session credential or OAuth key would be, well, it's not the responsibility of the object you want to edit to tell you whether or not you can edit it some time indeterminately in the future.

Perhaps the OAuth key is revoked or the session expires? Or perhaps you load something when not signed in, then sign in and want to perform actions against that object without reloading it? It seems easier to leave permissions granting and cues for such to the application to request separately as it needs to as part of maintaining session state or OAuth.

It could be a smarter layer of application- or domain-object inferred functionality (to hide or show certain actions) on top of a much simpler stateless structure (where all actions are offered regardless of state).

A question I would pose, with no right or wrong answer, is what about an API that's primarily read-only but to which admins have write access? Should read-only clients see actions they cannot perform as of moment? Despite potential confusion, I would argue yes, as we often see actions on the web which we are not allowed (e.g. commenting) that only prompt us for authentication after we show interest. In addition, it's likely better for understanding an API to reveal all actions at all times, as despite the added complexity, you can call the API under any circumstance and understand the totality of the API at any given moment, without having multiple accounts with differing permissions to view edge case actions.

As to some of the JSON I've seen in this GIST, I really feel like the more we go down this route and the uglier the JSON gets, the more I wonder if we should just embed simple JSON objects with an XML wrapper for navigation/context and be done with this nonsense. Play to the strengths of each, and put simple data in JSON and more complex data in XML. Though at that point you have to parse both, so maybe it's not as ideal either. Hmm...

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