cainus / hyper-json-spec

A specification for a json format with links
The Unlicense
6 stars 0 forks source link

Schemas by URI and adopting some elements of link hints #2

Open emmanuel opened 10 years ago

emmanuel commented 10 years ago

I haven't completely thought this through, but:

  1. I would like to refer to schemas by URI instead of enumerating complete schemas inline with each response.
  2. I like some of the ideas in the link hints IETF draft, and I think Hyper+JSON could benefit from incorporating them.

With that in mind, I drafted the following as a proposed refinement to the example on your blog:

{ "musicians": [ { "firstName": "Joe"
                 , "lastName": "Strummer"
                 }
               , { "firstName": "Neil"
                 , "lastName" : "Young"
                 }
               ]
, "_links": { "self": { "href": "/musicians"
                      , "hints": { "allow": [ "GET" ]
                                 }
                      }
            , "create": { "href": "/musicians"
                        , "hints": { "allow": [ "POST" ]
                                   , "accept-post": { "application/json": { "schema": { "href": "/schemas/create-musician"
                                                                                      }
                                                                          }
                                                    }
                                   }
                        }
            }
}

This presupposes that the schema accepted for creating a musician can be retrieved at /schemas/create-musician, which when requested with an application/json content type, would yield a response like:

{ "schema": { "id": "/create-musician"
            , "description": "A musician"
            , "type": "object"
            , "properties": { "firstName": { "type" : "string"
                                           , "title" : "First name"
                                           }
                            , "lastName": { "type" : "string"
                                          , "title" : "Last name"
                                          }
                            }
            , "required": [ "firstName", "lastName" ]
            }
}

A couple of notes:

  1. I produced the /schemas/create-musician by joining the link rel (create) with the singular resource name (musician). I think this is a decent (or even good) default approach for defining schema URIs.
  2. I think the hints key and its layer of nesting could be dropped, which would place allow and accept-post as siblings of (on the same level as) href, directly under the link rel key.
  3. The presented schema is intended to conform with JSON Schema Draft 04. I believe it does so as presented.

That is all. What do you think?

cainus commented 10 years ago

Thanks for the input! It's nice to finally get some outside feedback on this stuff. I saw this a few days ago, but haven't had a chance to give this a serious look yet. I'll try for this weekend though.

emmanuel commented 10 years ago

No rush.

I've been playing around with Percolator and related to that, I've been peeking around at a few JSON hypermedia content types. There's a lot I like about Hyper+JSON, but I was excited to see that the link hints draft provides some ideas that could be adapted/adopted.

Also I learned that JSON Schema supports (as of draft 4?) the notion of schema IDs (see tdegrunt/jsonschema and https://github.com/natesilva/jayschema) and $refs to schemas of embedded entities, which gave me the idea of something like $ref to reference a schema by URL instead of inlining it into the response.

Anyways, I'm looking forward to your thoughts.

cainus commented 10 years ago

Ooooh $ref is very cool. I like that. I'm not sure if the jsonschema parser in Percolator supports that, but I definitely think that's a great idea.

I'm curious though what do you think the advantage is of:

"self": { "href": "/musicians", "hints": { "allow": [ "GET" ]  }   }

... over:

"self": { "href": "/musicians", "method": "GET"  }   // GET is the default anyway, so usually method is omitted in that case

?

I have a much easier time selling the second one to people that are new to hypermedia apis. Any clue what the hints standard is trying to solve with the more wordy format?

emmanuel commented 10 years ago

I don't see any benefit other than bundling everything under the hints key. Frankly I was a bit confused about how to express certain things based on my reading of the hints draft. Specifically I flip-flopped a bit as to how schema URIs should appear. For example, this:

...
"href": "/musicians",
"method": "POST"
"accept-post": { "application/json": { "schema": { "href": "/schemas/create-musician"
                                                 }
                                     }
               }
...

Feels really wordy to me. I think the schema key could be moved up to being a sibling of href and method (as you had it in the blog post); I am a little uncomfortable with dropping the explicit content type, but I am struggling to satisfactorily answer the question: why would a client need/want to switch content types? My discomfort stems from questions like what is meant by no content-type expectation stated in the link info? does it mean that the client should continue using the current resource's content type? No answers, only questions. :)

cainus commented 10 years ago

I have to admit, I'm not 100% sure what the answer is for different types either. This is why the Percolator code currently allows a "type" property, but I haven't gone into it in the spec. I was thinking that if someone wanted to use schema (or uri-template?) to specify a "application/x-www-form-urlencoded" payload that might be possible too. Or if I wanted to specify a multipart/form-data content-type, or some other format with no schema ( image/jpeg? ) just for general document upload. Those seem like pretty common use-cases (though I haven't personally used Percolator that way YET).

The bottom line though is that IMO you only need a type on requests with a body, just like html forms do (and "application/x-www-form-urlencoded" is the implicit default, which is where I got the idea for an application/json explicit default).

Conversely, GET requests have no need for a type because they have no request body. I didn't intend the type to represent anything other than a request body content-type expectation because Accept headers are perfectly fine for handling response bodies.

I think this could work, but I'd like to try it out before I put it in the spec. What do you think?