Skip to content

Instantly share code, notes, and snippets.

@mdwheele
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdwheele/68079156771839ba8f3d to your computer and use it in GitHub Desktop.
Save mdwheele/68079156771839ba8f3d to your computer and use it in GitHub Desktop.
Ponderances on using media-type representation as a mechanism for partitioning use-cases on a resource under different contexts.

Please do not read any of this with an "absolute" tone. These are merely my thoughts on a problem based in my current understanding and some experience. To be honest, I would not be surprised if this is already being done by individuals more experienced than I at building REST APIs, but I think it is worth discussing as I have yet to find anything like this blogged about. I present this with utmost humility in order to foster discussion; not to create devolving argument.

Some Background

I believe that usage of media types in examples on the web demonstrating "how to build" RESTful APIs is too narrowly focused. In my experience, much of the discussion around them seems to be solely focused on their usage either in content-negotiation or applying versioning to an API.

As an aside, versioning can be "controversial" itself as an "anti-pattern" of REST. I heard it argued that HATEOAS should be the answer to the problem that versioning works to solve. This is an aside and not the focus of this ponderance so it's not worth discussion here. Versioning techniques currently discussed at-large are a practical way of solving a technical problem when strategies for offering RESTful API discoverability are not ubiquitous, yet.

Forgetting about versioning, Accept headers are commonly used to specify expected (accepted) representation of a resource when a response is made to a client. This is reasonable and makes sense, but I think it is still too narrowly focused because most examples stop at "Use this to render in json, xml, yaml, whatever."

What is a resource?

A resource is a >>uniquely identifiable abstraction<< exposed through an API that may allow various operations to be performed on it through HTTP verbs.

The resource itself is an idea separate from any technical implementation. It's an abstract concept that has some attributes and behaviour. When I ask a server for the application/json representation of a particular resource, the key word is representation. The server is transforming the abstract (or internal) concept of a specific resource into a concrete format that matches a client's expectations for consumption.

Consider a human in the world: /human/6P6KC7jfMk6CIO7J. This human has a name and a birth_date. Immediately after birth, this human matures and at some point attends primary school. Later they will graduate and may go to University or go straight into the workforce, but they will always have a name and a birth_date in the context of "being a human". However, people can be represented and be interacted-with in infinitely-many ways. In this horrid example we have:

  • A person in the context of being human (this is the abstract uniquely-identifiable resource)
  • A student in the context of attending a primary school
  • A student in the context of attending University
  • A laborer in the context of having a job
  • ...
  • A taxpayer in the context of being a citizen of some nation
  • A funeral attendee in the context of being deceased (morbid, I know)

When thinking this way, the representations of a single resource in any of these contexts will vary in attributes and perhaps, textual formatting. In addition to response representation, they will vary in the sub-resources they contain and, possibly, the commands that are executable on them (use-cases based on semantics of the custom media-type representation).

Media types become projections of a specific resource into different contexts

All of the following are requested against /human/6P6KC7jfMk6CIO7J

"Being Human (application/vnd.api+json)"

When approaching rich media-types, this becomes a "raw data model" that is less useful. I'll explain why below all these.

{
  "name": "Dustin",
  "birth_date": "04-09-1902"
}

"Having a Job" (application/vnd.api.worker+json)

{
  "name": "Dustin",
  "salary": "chips",
  "work_orders": [
    ...
  ],
  ...
}

"Being a student" (application/vnd.api.student+json)

{
  "name": "Dustin",
  "class": "Senior",
  "major": "Computer Science",
  "courses": [
    ...
  ]
  ...
}

So, when I stop thinking about response formatting and start thinking about semantics, I'm lead to identifying possible differences in the semantics of how I interact with resources and my ultimately my question:

Is it reasonable to separate use-cases on resources in different contexts through the use of rich media-types? This would include the API definition itself being contextually driven by the semantics of specific requests.

In addition, is there anything "being RESTful" has to say against something like this?

Under this type of usage, custom media-types can almost be thought of as a "third dimension" of a typical (as represented by examples on the web) "RESTful HTTP message".

Small example: A student adds a course to their schedule.

> POST /human/6P6KC7jfMk6CIO7J/courses HTTP/1.1
> Accept: application/vnd.api.student+json
> --
> {
>   "name": "How to Go+Ruby+PHP+Rust 101"
> }

--- Resource server determines that this is a request to execute 
--- a use-case on a student and handles appropriately.

< HTTP/1.1 200 OK
< Content-type: application/vnd.api.student+json
< --
< ...

The combination of a POST to the application/vnd.api.student representation of the /human/6P6KC7jfMk6CIO7J/courses sub-resource starts the use-case: "adds a course to the student's schedule". The resource server will have a specific use-case flow for this; complete with request validation, execution and response.

However, if we attempt the same use-case on a different representation (aspect/context) of a resource, we get an appropriate 4xx-level response. This happens because when we use a different representation, we're operating on a specific resource in a completely different context.

> POST /human/6P6KC7jfMk6CIO7J/courses HTTP/1.1
> Accept: application/vnd.api.worker+json
> --
> {
>   "name": "How to Go+Ruby+PHP+Rust 101"
> }

--- Middleware would determine that the worker representation has no courses sub-resource

< HTTP/1.1 404 Not Found
...

Other 4xx status codes could be justifiably used. In this case, I'm using 404 because the sub-resource doesn't exist but that's kind-of a cop-out honestly. We could probably find a way of responding in a way that is semantic to the requested use-case. Maybe another idea would be to respond with a 409 and a reasonble message explaining "why" there is a conflict ("Client is trying to add a course to a worker's class schedule. Doesn't make sense, buddy. Workers don't have class schedules.").

415 might make sense, but starts to speak about specifics you'd consider when reasoning about the HTTP specification in the context of a technology use to serve physical resources / assets through the web. This is where I think there's future discussion to be had and where I cut the initial dialogue. The first thing to discuss is whether or not it is "standard" or reasonable for resource representations to be used as a way of partitioning use-cases.

HTTP Status Reference

406 Not Acceptable

The resource identified by the request is only capable of generating response 
entities which have content characteristics not acceptable according to the accept 
headers sent in the request.

Unless it was a HEAD request, the response SHOULD include an entity containing a 
list of available entity characteristics and location(s) from which the user or 
user agent can choose the one most appropriate. The entity format is specified 
by the media type given in the Content-Type header field. Depending upon the format 
and the capabilities of the user agent, selection of the most appropriate choice 
MAY be performed automatically. However, this specification does not define any 
standard for such automatic selection.


409 Conflict

The request could not be completed due to a conflict with the current state of the 
resource. This code is only allowed in situations where it is expected that the user 
might be able to resolve the conflict and resubmit the request. The response body 
SHOULD include enough

information for the user to recognize the source of the conflict. Ideally, the response 
entity would include enough information for the user or user agent to fix the problem; 
however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if 
versioning were being used and the entity being PUT included changes to a resource which 
conflict with those made by an earlier (third-party) request, the server might use the 
409 response to indicate that it can't complete the request. In this case, the response 
entity would likely contain a list of the differences between the two versions in a 
format defined by the response Content-Type.


415 Unsupported Media Type

The server is refusing to service the request because the entity of the request is 
in a format not supported by the requested resource for the requested method.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment