Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does anyone actually use JSON Hyper-schema #48

Closed
awwright opened this issue Sep 15, 2016 · 101 comments
Closed

Does anyone actually use JSON Hyper-schema #48

awwright opened this issue Sep 15, 2016 · 101 comments

Comments

@awwright
Copy link
Member

There's a lot of broken features defined in JSON Hyper-Schema, I want to ask implementors how much I can be allowed to "break" (i.e. make compliant with normative references).

Mostly things like quirks about how it defines URI templates, uses "rel", and uses "method".

Anyone?

@handrews
Copy link
Contributor

From my point of view, I'd be happy to reboot the whole hyper-schema approach while preserving some specific elements. In my past work, we picked bits and pieces from JSON Hyper-Schema to assemble into an alternate approach that got broad buy-in where Hyper-Schema as defined did not.

That has a bit more to do with what Hyper-Schema should look like in v6, but it does indicate (for me, at least) that the v5 cleanup can be pretty aggressive since we should expect significant changes for v6 anyway.

@Anthropic
Copy link
Collaborator

Anthropic commented Sep 26, 2016

@awwright I use it for defining a link with rel of "options" to then pull in the options for a select.

Totally agree that it doesn't seem to be the best spec of the bunch and more than happy to re-work if there are changes. But just wanted to submit a use case for consideration.

@jdesrosiers
Copy link
Member

I use Hyper-Schema for all of my JSON based REST APIs (although I haven't had the opportunity in while). In fact, I would have very little interest in JSON Schema if it weren't for JSON Hyper-Schema. As far as I have been able to find, it is the only standard capable of doing hypermedia APIs in JSON properly (1).

In REST, resources should be self descriptive giving you everything you need to know about what it is and what you can do with it. On the web this takes the form of links and forms. Many JSON standards out there can describe links, but only JSON Hyper-Schema can do something like a form. If you are familiar with the Richardson Maturity Model (2), all the other options are designed for Level 2 REST APIs. JSON Hyper-Schema is the only Level 3 option around.

I created a little library a while back that allows you to create fully functional CRUD APIs by writing only Hyper-Schemas (3). These APIs can be used without any out-of-band knowledge using Jsonary's (4) generic Hyper-Schema browser. This library allowed me to walk through the workflow while designing APIs. Nothing else is capable of that.

This was a much more long winded response than I planned on, but my ultimate purpose is to say that I am strongly against changing the Hyper-Schema approach. There are certainly things that can be cleaned up, refactored, or extended, but I'll be keeping an eye on any Hyper-Schema proposals to ensure that the spirit of Hyper-Schema is not lost.

I'm all for the kind of changes it sounds like @awwright is talking about, but I am concerned about what I think I'm hearing from @handrews. I look forward to reading your proposals.

(1) JSON-LD has Hydra and XML has XHTML, but JSON has only JSON Hyper-Schema
(2) http://martinfowler.com/articles/richardsonMaturityModel.html
(3) https://github.com/jdesrosiers/resourceful
(4) https://github.com/jsonary-js/jsonary

@slurmulon
Copy link

slurmulon commented Oct 8, 2016

We use JSON Hyper-Schema extensively in both our platform API and client (really just JSON Schema in the back-end for validation purposes). I have worked on another enterprise API that was based on the Siren specification, but JSON Hyper-Schema is far superior due to its insane flexibility, its ability to keep things DRY and support for meta schemas ✨. I completely agree with @jdesrosiers that Hyper-Schema is the only JSON-based hypermedia API specification that is truly Restful. My favorite aspect is how it doesn't require you to modify your API responses, it's entirely non-invasive and complementary.

I'm not sure what issues you are having resolving your refs, but we have found Ajv to be very consistent and robust in resolving them. Many libraries, such as is-my-json-valid, expect the user to provide a self-managed object mapping $ref to schema.

The only "limitation" our team has encountered (really just an initial mis-understanding) has to do with entity instances that are required/used for resolving URI Templates into absolute URLs - the issue is that some APIs want to use deeply nested resources that require multiple entities, like so:

/api/v1/a/{uuid}/b/{uuid}

The questions we ran into were "Do we use the same entity instance? Another? If it's from another, how do we specify where the instance should come from?"

Our complications were mostly due to the fact that we designed our schemas to map nearly 1:1 to our API's domain model entities, such that each schema correlated to a single high-level entity (and I believe this is the general idea, but I'm sure there's other opinions on that). We still referenced other schemas with $refs and had normalized schemas, but the general idea was that the links defined in the schema only needed to know about the entity correlating to, well, that schema.

Because of this, it wasn't initially clear that support for working with multiple entity instances when resolving LDO (Link Description Object) URI templates is possible with JSON Pointer references - otherwise the URI template slugs correlate with the defacto entity instance provided (http://json-schema.org/latest/json-schema-hypermedia.html#anchor8). The question you still have to answer in your client, though, is what entity should be used when the reference is say #/foo but the user has multiple Foos to work with in the client - should it be the first, the last, the "selected" one by the user? I think it's fine to allow the application to handle this in whatever way they want, it's just not explicitly talked about in many JSON Hyper-Schema resources/tutorials.

Once our client-side API resolves the URI Template into a URL based on the provided entity instance, we then just construct an HTTP/REST resource caller that automatically follows whatever method is defined in the LDO

Edit: couple typos

@awwright
Copy link
Member Author

awwright commented Oct 8, 2016

@jdesrosiers Great feedback, thanks! As how REST has the code-on-demand constraint (which I take as including scripts and stylesheets), I sort of view JSON Schema as similar to a stylesheet, JSON Schema tells you how to render or how to work with the data you've been given.

One of my outstanding concerns with JSON Hyper-schema is it has a "method" property combined with "href" and "rel". A link is supposed to describe relationships between resources, not necessarily listing things to do. Retrieving information about an associated resource is GET, HEAD, or OPTIONS, changing a resource is PUT or PATCH, executing some remote function is POST.

Can you provide some more examples of how you'd like to approach linking?

I'm re-reviewing the Richardson Maturity Model, which is so great for describing what RESTful really means. I'm also reviewing RESTful Web APIs by Leonard Richardson & Mike Amundsen (the legends of hypermedia protocols).

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@jdesrosiers while I would happily reboot hyper-schema, a more incremental approach based on adding rather than changing is entirely possible.

Honestly, I've been saying provocative things about hyper-schema mostly to motivate people to actually speak up. "Resourceful" looks really interesting, I'll have to look into it more deeply. Part of my interests here have been around seeing if anyone has developed best practices for hyper-schema that mitigate the problems we found. If so, I am absolutely not dead-set on changing it.

I agree that JSON Hyper-Schema is the only thing that comes close to RMM Level 3. It is actually what helped me sell RMM Level 3 at Riverbed, but ultimately the team found hyper-schema too confusing to accept.

At a high-level there were three problems, ranging from annoyances to deal-breakers. I was going to wait and file proposed solutions (um... after I figured them out), but since we're seeing some life here, let's just look at the problems. Perhaps someone else figured out a way around these, or can explain what we missed along the way.

This is, of course, my personal opinion and recollection several years down the road, and not the official opinion of my employer at the time (or current employer).

Implicitly defined resources

Hyper-Schema implicitly defines resources by defining links to them. Every schema that has a self link is a resource (relatively explicit), and every link URI that is not otherwise accounted for in a self link is also a resource (it can be confusing to line these all up).

This means there is no straightforward listing of resources anywhere obvious. Unless every resource is accessible from the root, which is not the case in many non-trivial APIs. It also means that with link URI templates defined all over the place, it is easy to produce conflicts, which also plays into the next problem.

This is the problem that only some people considered a deal-breaker. I was not one of them, but there were very strong opinions about this. Our solution was to separate the declaration of resources and links from the schemas. Schemas could be defined inline within the resources, but usually were $ref'd in.

I would like to sort out best practices for making the resources more clear, but am not at all attached to the separate enumeration approach. I can see "fixing" this just by implementing the documentation generator in a way that pulls the resources out and enumerates them clearly in the docs.

Repetition in link definitions

There's a lot of repetition in link definitions, or else functionality left out that should be doable in one step. Simply put, if I can do all the usual CRUD stuff on books and people, and a book has a list of authors, all of the links I define on authors are redundant. They're already defined for people. But I have to re-define them with a slightly different URI template of "/people/{authorId}" instead of "/people/{id}", and re-define the schema non-authoritatively in targetSchema (which can at least be done with $ref). I either need to re-define every person link, or force the caller to first GET the author in order to discover that it can be DELETEd. However, many operations, including DELETE, should not require a GET first, so that's back to duplication.

What makes more sense when both resources are described in the schema is is to be able to define a relationship between books and people with a rel of "author" and rules for how to translate the book instance data into a form that can fill out the self link for "person." e.g. map "author_id" in book to "id" in person (assuming person's self link is something like "/people/{id}").

This approach doesn't technically make anything impossible, but it was considered excessively burdensome. The only way we would have been able to use JSON Hyper-Schema would have been to write a pre-processor that duplicated all of the links out to the proper places.

Our solution to this was just to add a "relation" field that referenced the other resource (it helped to have resources enumerated separately here, but is by no means required), and provided a map from the source schema's fields to the destination schema's self link URI template. This was done by the vars approach seen in issue #52 (extended href templating). Using the same vars mechanism in multiple places made things a lot easier- it is always the way to translate URI parameters when they don't line up perfectly with the URI template syntax.

[edit- removed the bit about href duplication from self as Hyper-Schema actually does that part fine and I just forgot]

Another benefit of this solution was that it removed the need for most targetSchema definitions. Instead, you just looked at the target resource's self link. The relevant security considerations are already described for self links. targetSchema is still available for when you want to link to something outside of JSON Hyper-Schema.

I like this solution very much, but would be happy with anything that satisfied the same requirements of eliminating the duplication and more clearly showing resource-to-resource relations.

Can't describe both URI parameters and a message body

This makes JSON Hyper-Schema unusable for me. There is only one schema in the LDO, which applies to the URI parameters for GET and the body for POST (and presumably for PUT and PATCH although they are not mentioned?). Unless I am totally misreading things (which I could be), this makes it impossible to perform operations with a message body on URIs with query parameters. This means no such operations on filtered collections (a powerful alternative to messy batch processors), or partial representations (selecting a lighter-weight format), or anything else involving non-hierarchical identifiers.

Our solution is the one described in issue #52 (extended href templating). We used URI templates for all query string parameters. URI template variables either mapped to points in the instance schema (by the default action or through vars), indicating that they used the same validation rules, or they were given a schema directly if they were parameters that must come from an external source.

The extended href templating proposal was already in existence, we just adapted it and standardized that the request schema never applies to the URI, it only applies to the request body. Again, without a solution for this, JSON Hyper-Schema cannot adequately describe my APIs.

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@awwright : your comment here

One of my outstanding concerns with JSON Hyper-schema is it has a "method" property combined with "href" and "rel". A link is supposed to describe relationships between resources, not necessarily listing things to do. Retrieving information about an associated resource is GET, HEAD, or OPTIONS, changing a resource is PUT or PATCH, executing some remote function is POST.

captures my key concern in 500% less words :-)

Basically, resources are not a first-class concept in JSON Hyper-Schema. They kinda just happen, and a lot of weirdness exists around that.

@awwright
Copy link
Member Author

awwright commented Oct 8, 2016

@slurmulon I'm looking at "rel"... the current draft wants to add four values to the IANA registry and I'm not sure all those are necessary.

As for nested resources, unfortunately I'm not really sure how to handle the more complex URI cases.

Consider, though, if you're listing an absolute URI in a URI Template, then you're sandboxing yourself inside that namespace. It means if I want to store instances of resources on my server, I have to modify the schema so it points to my server instead of yours.

So increasingly, I think the best design pattern is that JSON documents should provide pre-computed absolute URIs or URI references, and minimize computation. The only time I would use an absolute URI in a URI Template would be if it's globally assignable anyways, like "urn:uuid:{uuid}", "ni:///sha-256;{hash_sha_256}" and the like.

The other alternative is we provide some way to let a script compute a value on a JSON instance, including provide a list of link relations. This is certainly within the scope of REST, but it seems excessive.

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@awwright : We set a base URI for all of our schemas and then if the template began with $, the dollar sign was replaced with the base URI. This is similar to issue #46 (LDO baseUri) but at the scope of an entire API (a concept which JSON schema also lacks, although making a schema that is all definitions plus a schema for the entry point resource can more or less serve that purpose).

Anyway, this removed all need for absolute URIs for us except when going outside of the API entirely, in which case it was fine. We needed to be able to define URIs relative to the API base without having to repeat the API base (e.g. "/some/api/base/path" in "https://api.example.com/some/api/base/path") all over everything.

@slurmulon
Copy link

slurmulon commented Oct 8, 2016

@awwright Regarding nested resources and how they are currently handled, I stumbled across something like this (yes, with encoding):

/api/v1/user/{ root.json%23%2Fdefinitions%2Fuser }/quotes/{ root.json%23%2Fdefinitions%2Fquote }

I'm unable to find the resource for this, I ran into it months ago and it's very possible that it isn't officially supported (not seeing anything mentioned here: https://github.com/json-schema/json-schema/wiki/href or in the spec). Anyways, it's the closest thing I could find that would allow us to support multiple entity instances (our API team was annoyed with this fact, but I don't think it's a big deal at all). We had to write our own LDO href resolver and we just didn't add support for referring to other entities, particularly because of the complexities around "which one is the right entity?" The schemas define what an entity can be, not which one it is - and in my opinion this is a very good thing, it's just something for the client to consider in its state management/design.

I agree that you should minimize computation with your links, it will generally make everybody's life easier. I think though that it will still be necessary, even for common cases such as /api/v1/user/{uuid} - this prevents the need to modify the API response in any way (:heart:) - other hypermedia specs like Siren do not have this luxury. This strict separation also ensures that the API never needs to concern itself with any sort of state (specifically to answer "which entities should be used to make this link?"), satisfying HTTP and REST.

Even with globally unique identifiers, you still need to ask the question of "which instance do I use for the other entity/entities?" in the case of LDOs with more than one entity reference. With one entity it's trivial, because you can always just associate the schema/schema validator with whatever entity you want. But when there is a nested entity, it must either also be explicitly provided by the dev (increasing domain coupling, nipping away at a clean design) or implicitly provided by some state management system, and that (to me) is the hairy part. But again, I think this is primarily a concern of layers above JSON Hyper-Schema.

Edit: clarity, grammar

@slurmulon
Copy link

@handrews

Basically, resources are not a first-class concept in JSON Hyper-Schema. They kinda just happen, and a lot of weirdness exists around that.

I've noticed this as well, what do you think can be done to help alleviate/fix this? What if you say that an LDO method correlates with the resource's OPTIONS when the value is an Array (please excuse the rel here, clearly not sufficient):

{
  "rel": "self",
  "method": "GET",
  "href": "/v1/api/user"
}
{
  "rel": "updates",
  "method": ["POST", "DELETE"],
  "href": "/v1/api/user/{uuid}"
}

Otherwise, if it's a single value, method should be interpreted as the method that should be used (in other words there is only one "usable" method on the resource, so there's no point to provide a collection).

It seems this could help to eliminate redundancy in the LDOs as well since there's no need to duplicate links for additional actions that can be made on a resource. Perhaps the example I provided could be collapsed even further with URI Templates, such that GET, POST and DELETE can exist in the same LDO.

@awwright
Copy link
Member Author

awwright commented Oct 8, 2016

@slurmulon We could have a field similar to HTTP "Allow" that specifies which methods the resource is capable of handling. So if "PUT" is missing, then it's a read-only resource; if POST exists, then it's an executable resource; etc.

@handrews See how many of those concerns have issues filed against them? I should create a milestone for this maybe.

@slurmulon
Copy link

@awwright yeah love it, that way method doesn't have two different meanings depending on its value

@awwright
Copy link
Member Author

awwright commented Oct 8, 2016

Unlike jsonschema-core and jsonschema-validation, I'm at something of a loss for how to save jsonschema-hyperschema.

My best idea right now is I'll rewrite major sections of the spec, and won't be afraid to break reverse compatibility with existing implementations. These are just Internet-Drafts, after all, and I think this is warranted because HTML, Atom, HTTP Link, HAL, etc, have much bigger adoption and are far more consistent, and have probably seen better adoption because of their consistency.

These are the following features I'm looking to gently change or preserve, everything else I'll tear out unless someone says they'd like it:

  • links (how to extract hyperlinks - list of Link Description Objects)
    • anchor (e.g. "http://example.com/subject")
    • rel (e.g. "self")
    • rev (reverse link, e.g. "author")
    • href (e.g. "http://example.com/object")
    • hreflang (e.g. "en")
    • media (e.g. "screen" "print")
    • title (e.g. "Measurement Levels and Configuration: A Linear Approach")
    • type/mediaType (e.g. "application/json")
    • accept/methods (e.g. "GET, HEAD, OPTIONS, POST, PUT, PATCH")
    • schema
    • enctype
    • schema
    • targetSchema
  • media (how to interpret a string as an information resource)
  • readOnly (the instance is managed exclusively by the authority, don't bother changing it)

During my survey, I found or came up with these features that I think would be useful:

  • deprecation (provides information that an instance shouldn't be used and might be removed in the future)
  • base (changes the URI base of an instance to this value, after URI template processing)
  • forProperties, forItems, forAdditionalProperties, forAdditionalItems (descend into an array or object, but links still describe the current JSON instance)

I'll create issues for these enhancements.

Please let me know all your opinions!

@awwright awwright modified the milestones: draft-6, draft-5 Oct 8, 2016
@slurmulon
Copy link

@awwright Wow, I did not know that Hyper-Schema was in such a dire place - I have experience with HAL, JSON-LD, Siren, and more, and JSON Hyper-Schema is by far the most extensible and non-invasive hypermedia spec I have ever encountered. Its ability to represent and work with highly-complex data structures is simply unparalleled, and the fact that it establishes a unified validation language between layers of the stack is absolutely incredible (and generally long desired!) as it completely prevents any sort of code duplication or parallel business logic across layers. Its integration with things like API Blueprint allows you rapidly accelerate productivity with tools like Drakov (mock server) and Dredd (test your API Blueprint, and the JSON Schemas defined in it, against a real API). These are all amazingly helpful features that other specifications simply don't have, don't allow, or struggle with greatly.

I'm happy to do whatever I can to help keep the spirit alive (documentation, technical/design discussions, whatever). I believe a lot of this stems from a lack of documentation / examples that give people their "Aha!" moment. It took our team a minute to realize the magic of Hyper-Schema, but once our team did it was a no-brainer as to how much it could help, and it did.


As for the feature list you mentioned, I love the idea of a base URL/URI, this would help relieve a lot of the complications regarding $ref resolution and JSON Pointer

As for readOnly, wouldn't you just implicitly know that the resource is read only when the only available accept / methods is ['GET']? Perhaps that's too indirect, but could save the need for an extra property.

Regarding title - is this intended to be used as a presentation label for the link? IME things kind of break down once you get too far down HATEOAS (i.e. Browser in a Browser) because the server is now concerned with UI presentation, and it also complicates i18n/l10n/partnerification if say the text translations are handled on the client side, which is relatively common. Perhaps in this case gettext tokens can just be sent in title and the client would be expected to replace them, not bad I guess (thinking it's probably best to make this optional..?)

I really love the idea of "forProperties, forItems, forAdditionalProperties, forAdditionalItems (descend into an array or object, but links still describe the current JSON instance)" - this would allow semantic iteration through specific areas of an instance object, which is great!

This wasn't mentioned, but I like the idea of adding $ref to LDOs (in addition to targetSchema, because to my understanding this only applies to the API response instead of the request) since it would allow the client to pre-validate an object before sending it off to the API, allowing the client to save an extra network call.

@slurmulon
Copy link

@awwright @handrews I would love to join the organization if you're accepting members. I use JSON Hyper-Schema in many of my projects (including enterprise ones), so I am very passionate about the role that it can play in the Hypermedia world and have a lot of incentive to keep the ball rolling.

Please feel free to email me at me@madhax.io if you'd like to discuss this further!

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@slurmulon I don't have any official position here myself, I'm just talkative and opinionated :-)

@awwright
Copy link
Member Author

awwright commented Oct 8, 2016

@slurmulon Luckily I don't see the spirit of hyperschema changing at all, I think we're just realizing there's a small handful of things that don't mesh well with current hypermedia design best practices.

title is a standard link parameter, RFC5988 says:

The "title" parameter, when present, is used to label the destination
of a link such that it can be used as a human-readable identifier
(e.g., a menu entry) in the language indicated by the Content-
Language header (if present). The "title" parameter MUST NOT appear
more than once in a given link-value; occurrences after the first
MUST be ignored by parsers.

readOnly is mostly for individual (sub-)instances, so you can say particular properties in an object are managed by the authority (the server). Things like serial numbers and computed values, where it only makes sense for the server to assign or update and not anyone else.

Regarding $ref in LDO, can you file a new issue that includes some examples and use cases?

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@slurmulon and @awwright : I'm extremely confused by both of these things:

forProperties, forItems, forAdditionalProperties, forAdditionalItems (descend into an array or object, but links still describe the current JSON instance)

/api/v1/user/{ root.json%23%2Fdefinitions%2Fuser }/quotes/{ root.json%23%2Fdefinitions%2Fquote }

Are these attempts to use parts of the instance data other than the immediate properties for resolving the URI template variables? Or for using instance data from within elements of a list or properties of an object within an instance?

If so, those problems are solved very cleanly by the advanced templating proposed in in issue #52 which we used extensively throughout a set of APIs involving about 200 or so resources. I'm honestly baffled at the lack of reaction to that proposal, as it's the single most essential proposal outstanding for hyperschema for me (among other things, it is necessary to solve the failure to handle both URI parameters and message bodies, and (with relative JSON pointers) it handles URI parameters far more flexibly and elegantly than anything else I've seen proposed- no awful %37elf mess.

@slurmulon
Copy link

@awwright yeah "spirit" probably wasn't the best word choice - I guess I meant "momentum" since the title of this issue is "Does anyone actually use JSON Hyper-Schema?" hehe

title is a standard link parameter, RFC5988 says:

Cool, that makes sense. I was making a wrongful assumption of how the value would/could be used.

readOnly sounds great as well - is this expected to be a simple boolean or say a collection of sub-paths (i.e. JSON Pointer) to mark as read-only?

More than happy to file an issue that includes some examples and use cases. Stepping out for a few hours right now but will get to it later tonight/

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@slurmulon You put readOnly throughout your schema as a boolean on properties/items/whatever that need to be readOnly. We used this a lot at Riverbed, very useful (I can point you to public example schemas if you'd like to see how we used it).

@slurmulon
Copy link

@handrews regarding the work you did for #52 looks extremely useful and elegant - speaking for my own example, yes, it's an attempt to use parts of the instance data to resolve URI Templates (and instance data that potentially correlates to another schema, which raises the difficult question of "which entity instance should I use?"). I'm happy to help create examples, justifications, whatever, if you think that might help get things moving

I would like to see how you are using readOnly, we really hadn't made that consideration in our platform and our API sort of just ignores setting read-only values like UUID, so I'd love to see how we can stop doing that and instead leverage JSON Hyper-Schema

@jdesrosiers
Copy link
Member

I was hesitant to comment on this issue because I don't have the time to adequately keep up with the discussion. So, I'm sorry, I won't be able to answer all of the questions or comment on all of points made (at least not in a reasonable timeframe). For now, the following is how I think about Hyper-Schema in response to @awwright's inquires. As for specific difficulties with Hyper-Schema, @handrews, I'll try to address that at a later time if I can.

When thinking about REST architectures, I find it useful to make analogies to it's reference implementation, the web. However, it is important to remember that the web doesn't define REST, REST describes the web (for the most part). So, the analogy is not always perfect.

A JSON document is analogous to an HTML document. A Hyper-Schema can be linked in much the same way as a stylesheet, javascript, or image to completely describe the resource. This is where Hyper-Schema most deviates from the HTML analogy. It would be like if all the links and forms in your HTML were defined in a separate resource. It's different, but I've tentatively convinced myself that it doesn't violate any REST principles and it definitely has it's advantages. To make the analogy more clear, it helps to think of the analogy to HTML as the JSON document + the Hyper-Schema.

The next part of the analogy is hyperlinks like the anchor tag <a\>. This is the thing that most hypermedia offerings are designed to describe. @awwright, I think you are viewing Hyper-Schema links (LDOs) from this lens and thus are under the impression that they are doing too much. Although LDOs are capable of describing a hyperlink, they are not an analogy for hyperlinks.

LDOs are an analogy to an HTML <form\>. All of the things that you recognize as not belonging as part of a hyperlink, do belong in a form. There is not a hyperlink analogy in Hyper-Schema because the functionality of a form subsumes the functionality of a hyperlink. Therefore, it was unnecessary to describe them separately. This form-like functionality is the killer feature of Hyper-Schema that no one else has. I have to be careful with this part of the analogy because you shouldn't take this to mean that you necessarily should be able to generate a pretty UI for an LDO. This is the machine-to-machine version of a form.

One last analogy is Web 1.0 vs Web 2.0. Web 1.0 describes a web that is mostly static. It is mostly driven by hyperlinks and there are very few opportunities for users to interact or contribute content. Web 2.0 describes a web that is dynamic and makes heavy use of forms to interact with users. Other hypermedia solutions are capable of describing Web 1.0 style APIs were resources are largely read-only. Hyper-Schema allows a Web 2.0 style API that is naturally interactive. By naturally, I mean that there is no out-of-band information necessary to use it. That means a Hyper-Schema driven API needs no documentation to use it in the same way a website needs no documentation. Everything you need to know to use the website is included in each request.

Ok, I lied, I do have one more analogy. targetSchema was mentioned a few times. If I have my way, this keyword would be removed entirely. I know it is defined to be non-authoritative, but I think it's presence just enables and confuses people who still have not completely gotten away from the RPC mindset. When you click a link on a webpage, other than the URI, you don't get a guarantee or even a hint at what you are going to get. Whatever resource you end up with tell you what kind of resource it is and what you can do with it. You don't need to know ahead of time. This is one of the oldest concepts baked into the web and it is one of the things that has allowed it to scale the way it has.

One last thing, @handrews, I don't understand your position that Hyper-Schema is not resource-oriented. All interactions are done through interacting with a resource. The resource itself describes what you can do with it. What could possibly be more resource-oriented?

@handrews
Copy link
Contributor

handrews commented Oct 8, 2016

@slurmulon : For the examples, I'll send you info offlist- anyone who also wants info just comment or or start a thread on the Google Group. If anything interesting comes out of offlist discussions I'll post a summary back here.

I am a bit confused still about "instance data that potentially correlates to another schema". Do you mean the schema for a different resource, or do you mean a parent or child schema of the one defining the link?

To give credit where credit is due, the extended templating proposal was originally from Geraint Luff. We built on that with the "relations between resources" concept I mentioned earlier, but it was mostly already proposed.

@slurmulon
Copy link

slurmulon commented Oct 8, 2016

@handrews thanks for adding me to the Google Group, sounds good.

Regarding "instance data that potentially correlates to another schema", consider this URI template:

/api/v1/user/{uuid}/quote/{uuid}

If we only had /api/v1/user/{uuid}, it's completely trivial which object we want to pluck uuid from - a valid user object that's being provided to whatever method resolves our LDO URI templates.

When there is more than one identifier slug, such as with /api/v1/user/{uuid}/quote/{uuid}, it now becomes ambiguous which schema or entity instance object uuid is supposed to map to (#/user or... what?). The only time you don't have to ask this question is if the user response always includes a denormalized quote, because then you could just do something like this:

/v1/user/{uuid}/quote/{quote.uuid}/

But if you want to keep your schemas and API resources relatively normalized (which I generally do, not sure if most people feel this way), nested identifiers introduce a problem. This is why the syntax I mentioned above, using the encoded JSON Pointers, answers this question at least partially, the question of what schema should this URI Template slug correlate to. It does not, however, answer the question of which entity instance to use - this needs to be answered by the developer for sure, but the question becomes much harder when there are two entities to juggle instead of one in the client.

@handrews
Copy link
Contributor

@Relequestual I include Hyper-Shema keyword in the response schemas, but not in the response instance data. That lets me use links from the response document, without the links appearing inline in the response.

@handrews
Copy link
Contributor

handrews commented Mar 15, 2017

@Relequestual actually it's more accurate that I define a resource's representation, including its links, under "definitions" and then always "$ref" that schema everywhere its needed, including requests and responses. This means that the links are always present wherever the representation is referenced.

So something like this, where I define a collection resource, an item resource, give them both "self" links and the appropriate side of the standard "collection" and "item" links, and use a custom link to connect the entry point to the collection:

{
  "definitions": {
    "identifier": {
      "description": "32-character hash identifier",
      "type": "string",
      "minLength": 32,
      "maxLength": 32,
      "pattern": "[0-9a-f]+"
    },
    "foo": {
      "$id": "#foo-item",
      "type": "object",
      "properties": {
        "id": {"$ref": "#/definitions/identifier"}
      },
      "links": [
        {
          "rel": "self",
          "href": "/foos/items/{id}",
          "hrefSchema": {
            "type": "object",
            "properties": {
              "id": {"$ref": "#/definitions/identifier"}
            }
          },
          "targetSchema": {"$ref": "#foo-item"}
        },
        {
          "rel": "collection",
          "href": "/foos",
          "targetSchema": {"$ref": "#foo-collection"},
          "schema": {"$ref": "#foo-item"}
        }
      ]
    },
    "foos": {
      "$id": "#foo-collection",
      "type": "array",
      "items": {
        "allOf": [
          {"$ref": "#foo-item"},
          {
            "links": [
              {
                "rel": "item",
                "href": "/foos/elements/{id}",
                "targetSchema": {"$ref": "#foo-item"}
              }
            ]
          }
        ]
      },
      "links": [
        {
          "rel": "self",
          "href": "/foos",
          "targetSchema": {"$ref": "#foo-collection"},
          "schema": {"$ref": "#foo-item"}
        }
      ]
    }
  },
  "link": [
    {
      "rel": "tag:example.com,2017:foos",
      "href": "/foos",
      "targetSchema": {"$ref": "#foo-collection"}
    }
  ]
}

@Relequestual
Copy link
Member

Hyper-Schema keywords are included in the JSON Schema, not the response body.
@jdesrosiers

Thanks, that's what I though. I was pretty sure that was the case, but some of the discussion here confused me.

@Relequestual
Copy link
Member

I include Hyper-Shema keyword in the response schemas, but not in the response instance data. That lets me use links from the response document, without the links appearing inline in the response.

@handrews

I'm not sure I totally understand the phrasing you've used there, but I'll paraphrase what I understand when in conjunction with your example and you can tell me if I'm getting it or not.

You include the collection and single link in both the collection and single definitions, so it's clear how to get from one to another. Cross referencing them in a sense. Right?

My only query from that schema is what you're trying to imply by having...

          "targetSchema": {"$ref": "#foo-collection"},
          "schema": {"$ref": "#foo-item"}

schema key word is about payload, right? Are you trying to imply that that URL is for both getting the collection of items, but also for posting / creating a new individual items? I would expect (and have done for my schemas to work with doca) define two seperate link elements for getting and posting, specifying the method.

Maybe this conversation should be taken sideways into email... Breaking my own rule of clogging up an issue... but it's a closed one so not so bad I guess.

@handrews
Copy link
Contributor

@Relequestual @jdesrosiers I realized I did part of the example wrong. The "item" link should be from each item within the collection, not from the top level of the collection. That should make a lot more sense :-)

@Relequestual:
On the collection resource's self link, and the item resource's collection link, "targetSchema" is the collection. So if you GET on that link, you get the collection. If you PUT, you need to PUT the entire collection (unusual, but there are use cases for it and a collection is just another resource anyway).

On the item's self link, and each collection element's item link, "targetSchema" is the individual item resource. That's what you GET or PUT. Or PATCH, but that's a bit more complicated so I'm ignoring it for now. Let's get clear on the rest of it first.

On the item's collection link, and the collection's self link, "targetSchema" is the collection. Again, you GET and PUT that. But "schema" is the item.

An IANA "collection" link relation implies that a POST to the resource of an item representation will create a new item. While not mandated by the collection/item link relation RFC, this collection usage is basically the only design convention that's universally agreed-upon in all interpretations of "REST". An API should document that it does indeed support that, but this hyper-schema tells you everything you need to know to understand this.

The collection's "self" link has the same "schema" for the same reason, although at the "self" link relation does not determine anything about how POST is used. You need to notice that the "schema" schema has a "collection" relation that points back to yourself. Or notice that your "item" links points to the same thing as the "schema" schema. This is where things get a bit dodgy, and we need to put in more work. It needs to be fairly easy to recognize this sort of thing, but right now it's not entirely clear how that should work.

@jdesrosiers
Copy link
Member

@handrews, it looks like we take somewhat different approaches. Your example looks like a service definition approach to me because you have everything defined in one big hyper-schema. What schema do you link to when someone requests a single foo? Do you duplicate #foo-item? Or, does it always refer back to the entry point schema somehow?

Below is how I would refactor your example to align with my approach. Each resource has a hyper-schema that applies to it. You might notice that I took out all uses of targetSchema. The reason I did that is because if it is a self link, it is understood that the targetSchema is the current schema. The explicit recursive reference is redundant.

  1. A request to api.example.com/ is described by the following hyper-schema. It defines a link to /foos.
{
  "link": [
    {
      "rel": "tag:example.com,2017:foos",
      "href": "/foos"
    }
  ]
}
  1. If the /foos link is followed, then a collection resource is returned and is described by the following hyper-schema. It defines a self link which can be used with GET, PUT, or DELETE where the targetSchema is understood to be this schema. It also defines a create link for adding a new foo. Because this schema references the foo-item schema, it has those links as well, including a link retrieve an individual foo.
{
  "$id": "http://api.example.com/schemas/foo-collection",
  "type": "array",
  "items": { "$ref": "/schemas/foo-item" },
  "links": [
    {
      "rel": "self",
      "href": "/foos",
    },
    {
      "rel": "create",
      "href": "/foos",
      "method": "post",
      "schema": { "$ref": "/schemas/foo-item" }
    }
  ]
}
  1. If an individual foo is requested from a /foo/elements/{id}, it is described by the following hyper-schema. This includes the multipurpose self link as well as a link to the collection it belongs to. If the user follows the collection link, they go to part 2 where they have a collection and it's hyper-schema.
{
  "$id": "http://api.example.com/schemas/foo-item",
  "type": "object",
  "properties": {
    "id": {
      "allOf": [{ "$ref": "#/definitions/identifier" }],
      "readOnly": true
    },
    "foo": { "type": "string" }
  },
  "required": ["foo"],
  "definitions": {
    "identifier": {
      "description": "32-character hash identifier",
      "type": "string",
      "minLength": 32,
      "maxLength": 32,
      "pattern": "[0-9a-f]+"
    }
  },
  "links": [
    {
      "rel": "self",
      "href": "/foos/items/{id}"
    },
    {
      "rel": "collection",
      "href": "/foos"
    }
  ]
}

In case it wasn't clear, when I said "a resource is described by" I mean it has a Link header with rel describedby that can be dereferenced get a hyper-schema that can be applied to that representation (thus adding links to the response).

@Relequestual
Copy link
Member

See, this is my problem. Just two people having different interpretations of what's expected by default. If it's not "well done" or unclear, then why leave it abigious when the spec allows you to be unabigious? I'll happily share my JSON Schema files when our API goes "live" to show you how I've done this, but as of right now I cannot share them.

@handrews
Copy link
Contributor

@jdesrosiers There are a few things I just didn't convey well or did mistakenly:

  • I just wrote a single schema for convenience. In a real system, the schemas will (almost certainly) be per-resource. There are some things to work out in terms of re-use vs per-schema versioning, but per-resource is the starting point.

  • I don't even know why I put "targetSchema" on the self link. You are right that it is unnecessary and best left off, and I have written it that way elsewhere before.

  • I'm with you on the "describedby" usage.

I'll comment again in a bit about where I still see differences. I need to think through the points I want to make a bit more first.

@handrews
Copy link
Contributor

@Relequestual I want to separate two different sorts of concerns:

  1. True hypermedia APIs are still not a well-understood thing, and designing them is challenging no matter your media types.
  2. JSON Hyper-Schema does not yet provide all of the tools necessary to implement a comprehensive hypermedia design.

The first set of problems are not things that we can or should solve as part of JSON Hyper-Schema. An example relevant here is that IANA-registered link relations are often intentionally under-specified so that they can be used in a very broad set of circumstances.

The most significant difference between my schemas and @jdesrosiers's is in the choice of link relation type and the positioning of the links. I'm making an argument about what sort of assumptions you can make based on the presence of the IANA-registered "collection" and "item" link relations, even though the RFC that defines those relations does not require those assumptions, and therefore does not guarantee that they will always be correct.

But that has nothing to do with JSON Hyper-Schema. We could use XML and still find ourselves debating whether "collection" and "item" convey all of the necessary information.

What might be relevant is a question of whether JSON Hyper-Schema (or XML, or whatever) has or should have a mechanism of disambiguating standard link relation usage. Or describing custom link relation usage. Which I think is where @jdesrosiers's Relation Description Object proposal is coming from. But that's not specific to the choice of "collection" and "item" alone vs using a "create" relation.

Once you split the ambiguity up like this, it becomes clear that it's not a fundamental problem with JSON Hyper-Schema. Figuring out what assumptions can be made for a given link relation is independent of media type. Specifying assumptions is potentially part of the media type, but not specific to these link relations. And the differences in approach here come down to choice of an under-specified standard link relation vs a fully specified custom link relation (albeit a highly intuitive one).

@jdesrosiers
Copy link
Member

@Relequestual, I think the problem is that there are no tutorials out there showing how it is supposed to work. This is especially true of draft-05 which was a significant change. My approach has been shaped primarily by one principle: It has to work in the Jsonary browser. If I can't navigate the entire API just by following links and filling in forms, then I'm doing something wrong.

Here is the example we have been discussing as a simple static site http://hyper-foo.s3-website-us-west-1.amazonaws.com/?url=example.json. None of the unsafe methods are supported, but those links are defined so you can see it work. Also, the schemas are modified a bit because it only supports draft-04.

Jsonary was created by the creator of JSON Hyper-Schema, so I see it as the reference implementation for the standard (draft-04 at least).

@jdesrosiers
Copy link
Member

@handrews, thanks for the clarification. It sounds like we are on the same page (or at least very close) after all.

@handrews
Copy link
Contributor

@jdesrosiers yay! :-)

As I alluded to in the last comment, I think our difference is mostly about choosing which link relations to use and where to put them, and about how much we can assume from the presence of a particular link relation. While that's somewhat orthogonal to JSON Hyper-Schema itself, let me go into a bit more detail because I think the "create" relation in this case in part motivated by a weakness in Hyper-Schema as it currently stands.

In general, I avoid relations like "create" that are actions, because the relation type name should describe the nature of the relation between the resource, rather than directly specifying a thing you can do with the resource.

The important part of this is in avoiding having to specify one LDO per operation on the same target resource. This is where I think Draft-04 went wrong with its use of "method" to explicitly state the exact HTTP method. The use of the HTML-ish "get" and "post" without exactly meaning "get" and "post" was (in Draft-00 through -03) and is (in Draft-05 and -06) a problem, but that was not the correct solution. BTW I recently updated my detailed analysis and history of "method" in Hyper-Schema to cover Draft-06 and better cover Draft-05.

For any link with an HTTP URI scheme (with or without the -S, also CoAP or anything else with an explicit mapping to HTTP), a link indicates that the following are possible:

  • GET the resource, it should give its schema as the same one that's in "targetSchema", if any
  • PUT the resource, using the "targetSchema" for the request
  • DELETE the resource
  • If "schema" is present, POST to the resource, using "schema" for the request

PATCH is more complicated as it should be expressed in terms of a patch media type, e.g. application/json-patch+json or application/merge-patch+json, and we don't have a good way to convey that right now.

We do not currently have an effective and well-designed way to express in Hyper-Schema how much of the above is actually supported, or if the resource behaves in any non-standard manners that violate these exceptions. A hit at the values of the "Allow" and "Accept-Patch" headers would fix a lot of that "Allow" would take a list of HTTP methods, to avoid duplication. If we had an "Accept-Patch" hint, or if clients did a HEAD to get the runtime "Accept-Patch", then the patch media type plus the "targetSchema" should tell us what we need to know, because patch media types tell us how to structure a patch for a given document.

This is why I do not think that Draft-04 or Jsonary are the right approaches. Both (along with nearly all documentation systems I have seen) are too operation focused. It's still leftover RPC. It should be resource-focused, and the possible operations are derived from the protocol indicated by the URI scheme. POST has no standard for describing its request format, so it is given explicitly. The only thing we are missing is a way to indicate design-time choices to disallow some methods/operations. Although...

Per RFC 5988, a link relation MAY specify that it implies certain operations (mapping to HTTP methods in this case) are or are not available. This is where I get the notion of assuming that a "collection" relation with a "schema" is telling me that I can create an item via that link. POST-to-create-in-collection is the only design pattern that is universally referenced by every guide to REST ever written. While the "collection" link relation RFC doesn't require it, I'd argue that anyone who violated that pattern in a RESTful API deserves whatever problems they get :-P

Note that not supporting creation and returning a 405 Not Allowed is fine. That's not violating the pattern, that's just opting not to implement it (a read-only collection). But specifying "schema" on a link with relation "collection" and having the associated POST do something else is just a horrible idea.

So where does that leave us? Well, I hope you can see why I am treating "collection" that way and not writing an explicit "create" relation, even if you don't find it compelling enough to adopt yourself. But mostly I agree with you that we lack tutorials and realty-tested best practices. For that, we just need to keep working on making this specification real, implemented, and running.

@jdesrosiers
Copy link
Member

@handrews, I agree with almost all of what you say. Just two points where I disagree.

First, I don't share your concerns about the "create" relation. It looks like you are defining an action, but it can also be interpreted as defining a relationship to a resource that can be used to create something. Also, to be clear, this is the "create" relation that was defined in draft-04, not just some random description. However, I agree that "collection" could be used as well in this case. It may even be better. I'll have ponder that one a little longer.

But that brings me to the other point I disagree with: the idea that the "collection" link can be expressed as a single LDO. How schema is interpreted depends on the value of method. Without a value for method, the behavior of schema is undefined. You could do this ...

{
  "rel": "collection",
  "href": "/foos",
},
{
  "rel": "collection",
  "href": "/foos",
  "method": "post",
  "schema": { "$ref": "/schemas/foo-item" }
}

But, if you have schema without method,

{
  "rel": "collection",
  "href": "/foos",
  "schema": { "$ref": "/schemas/foo-item" }
}

there is no reason method should be interpreted as post. It could be interpreted as get which would make it a completely different link. Actually, I don't think it should be interpreted as anything and schema should be ignored.

@handrews
Copy link
Contributor

@jdesrosiers:

This gets back to "method" being a confusing mess that needs removing. I should have included "method": "post" in all of my links in order to be unambiguously compliant with Draft 06 (I'll explain why that works in a bit). In Draft 06, the only thing that "method" does is determine whether "encType" and "schema" apply to the URL query string (as named params) or to the request body.

In Draft 06, the form-encoded URL query string and any and all other portions of the URI are more flexibly and comprehensively specified by "hrefSchema", which defines a schema for the URI Template variables. A template of "/foos{?offset,limit}" with an "hrefSchema" defining "offset" and "limit" properties in Draft 06 behaves identically to a "schema" defining "offset" and "limit" with a "method" of "get" in Draft 04.

Crucially, "hrefSchema" and "schema" can be used at the same time. This allows both defining a schema for URI query parameters (and other URI Template variables) and defining a schema for a request body.

So in Draft 06, there is never any reason to use "method": "get" except for backwards compatibility. The only truly useful value of "method" in Draft 06 is "post", so I tend to forget and leave "method" out entirely. Because it's confusing. You don't need it (because only one possible value is useful) and the values do not mean what people think they mean.

I have argued and continue to argue that having a keyword called "method" that takes two values that appear to be HTTP methods but are actually not HTTP methods is a horrible confusing approach that needs to be removed ASAP, and I will take up that cause again in Draft 07. It is also a problem for documentation or static inspection that JSON Hyper-Schema does not offer a mechanism for hinting at HTTP method allowance (hinting because the resource can always return 405 Not Allowed, possibly transiently due to application state). I also plan to revisit that in Draft 07.

But the documentation/hinting of HTTP method usage and the specification of URL query parameters vs request bodies are completely orthogonal problems, no matter what the HTML history is. HTML forms were designed to abstract the submission mechanisms away from human users, and have never evolved beyond their original limitations due to the rise of JavaScript. Web apps that want to do something other than the behaviors allowed by HTML forms get around it with JavaScript. That doesn't make any sense for JSON Hyper-Schema. It should be fully functional on its own, without requiring workarounds through code-on-demand.

Anyway, a Draft 06 "method": "post" link can be used with any and all HTTP methods, assuming you are respecting proper HTTP semantics (supporting abuse of HTTP semantics is a real-world practical issue and we'll need to look at that, too, but let's punt that for the moment):

  • GET does not use "schema", as a GET payload has no defined semantics
  • PUT does not use "schema", as Draft 06 explicitly notes that "targetSchema" should be used
  • PATCH does not use "schema", and isn't currently well-supported by JSON Hyper-Schema
  • DELETE does not use "schema", as a DELETE payload has no defined semantics
  • POST uses "schema", as (unlike PATCH), there is no other way to describe its request structure

The request format for PATCH is determined by "targetSchema" plus the media type rules for the type(s) given in the "Accept-Patch" HTTP header. JSON Hyper-Schema doesn't provide a way to hint at or document "Accept-Patch", which is the same problem as the HTTP method hinting, which corresponds to the "Allow" HTTP header. We need to solve those problems, which is the right way to support PATCH (instead of overloading the use of "schema").


This is how linking works in most, if not all, hypermedia systems.

  • RFC 5988 HTTP "Link" headers: No method. "rel" can imply restrictions on the method, but there is no direct hint.
  • HTML <"link" /> element: No method.
  • XML links: No method (that I can find- I might be wrong, there are five billion XML specs, it seems)
  • JSON HAL: No method.
  • Collection+JSON: Method not directly specified. Template can be used for PUT or POST

There are other JSON-based things that start describing methods, but they (and, I argue, Draft 04 of JSON Hyper-Schema) are confused about the difference between hypermedia (ensures that links can be recognized with enough protocol and link relation type information to allow a client to know how they could be used) and API specifications (describes the exact HTTP usage allowed for each instance of a link)

We need to figure out something about API specification functionality, because people want it. But it is not part of hypermedia, and we need to keep those concepts separate. This could be done through another JSON Schema vocabulary, or it could by done the way the validation and metadata keywords are handled in the JSON Schema Validation specification: each keyword is clearly designated to function as either a validation or a metadata keyword. No keyword conflates the two.

I think that ensuring that we separate those concerns, whether by vocabulary or just be documenting them as having different purposes, we will avoid a great deal of the confusion that is currently present. And perhaps that will also help us figure out a way to support APIs that abuse HTTP semantics without impacting either proper HTTP usage or documentation.

@handrews
Copy link
Contributor

@jdesrosiers Also, I forgot "create" was in Draft 04 (it and "instances" were removed in Draft 05). "instances" is obviously redundant with "collection". "create"'s closest IANA-registered analogue "create-form", which is a better link name (relation, not action).

I think the "create-form" target resource needs to be the form rather than the resource to which the form is submitted, so that type of link would probably point to the collection's schema, because its "self" link's "schema" would be the form in question. I find just recognizing the "collection" link relation and applying the POST-to-create pattern more consistent, but I also see room for disagreement there.

@jdesrosiers
Copy link
Member

It seems that we have different interpretations of how the schema keyword is supposed to be applied.

The way I see it, the schema keyword is a non-optional part of the link. You can't decide to ignore it because you are making a GET request and a GET isn't supposed to have a body. However, an HTTP client should recognize that it doesn't know how to construct a request for a link with a schema using anything other than POST and behave accordingly. (Our disagreement really had nothing to do with method, so I'll ignore that diatribe unless there is some part you specifically would like me to respond to. We are mostly on the same page.)


After some more thought about "create" vs "collection", I've decided that I very much prefer your pattern of using "collection". However, I can't get over the fact that it is undocumented behavior. It doesn't matter how intuitive or widely accepted the pattern is, we can't just assume a relation that was clearly and formally defined also has this other behavior. It would be nice if "collection" defined this behavior, but since it doesn't, the options are to create a new pair of "collection" / "item" -like relations, or use the existing relations and augment their behavior with something like "create".

I don't think that "create-form", as defined, can really fill the role that "create" played. A resource that is related by "create-form" is supposed to be a resource that contains a form. That doesn't really help. If we have a creation link on the foo-collection schema, we could have a "create-form" link on the foo-item schema that points to the collection. That IMHO is a proper use of "create-form". That doesn't mean I don't like your interpretation. I like the idea, I just think it differs from the intended semantics of "create-form". Of course we could always create a new relation with those semantics if we wanted. Maybe something like "create-schema".

@handrews
Copy link
Contributor

@jdesrosiers

The way I see it, the schema keyword is a non-optional part of the link. You can't decide to ignore it because you are making a GET request and a GET isn't supposed to have a body.

Could you cite some motivation for this view in Draft 06? The JSON Schema specification is our specification. It means what we way it means. If there is something in Draft 06 that implies that "schema" must be used in this manner, that is a bug and we can fix it. It has been discussed multiple times in the process of writing Drafts 05 and 06 that a single LDO is intended for use with multiple HTTP methods, so the intent is clear to me. We just need to make sure that the intent is clear to the average reader of the spec.

However, I cannot find any such statements looking over Draft 06 right now. "targetSchema" explicitly states that it has different uses with HTTP GET vs PUT (capitalized so that they are clearly distinct from the explicitly lowercase "get" and "post" values of "method"). "schema" just doesn't say anything one way or the other. It is documented in the form of "if there is user input and method is X then user input is handled in way Y", which is not the same thing as "the presence of this keyword forces there to be user input." If you read it differently, please indicate exactly which parts imply required behavior so we can change them.

After some more thought about "create" vs "collection", I've decided that I very much prefer your pattern of using "collection". However, I can't get over the fact that it is undocumented behavior. It doesn't matter how intuitive or widely accepted the pattern is, we can't just assume a relation that was clearly and formally defined also has this other behavior.

This is exactly why I say that I see room for disagreement here. But this also comes up over and over with IANA-registered link relations. They are intentionally broadly defined, and will continue to be intentionally broadly defined. I don't know what the solution is (other than making up more "create"-ish relation types). RFC 5988 explicitly forbids treating combinations of relation types differently from relation types that are present separately, so "collection" + "create" should not be treated differently from "create" or "collection" individually.

One option is to document the usage of underspecified link relation types in a given API, but it's not clear to me how that fits with generic clients. It's not truly generic if you have to read non-standardized documentation.

This exact ambiguity is where I am struggling most with how to approach hypermedia.

@jdesrosiers
Copy link
Member

I responded to some of this in #276, so I won't repeat that here.

"targetSchema" explicitly states that it has different uses with HTTP GET vs PUT

And it has no behavior with DELETE. The fact that the behavior of targetSchema is coupled to the HTTP methods just reinforces my belief that targetSchema is broken (since draft-05) and should not exist (since always). There is some grounds for your interpretation of schema based on the way targetSchema is defined, but I think it would be building upon a wart in the specification and not the direction we should be going.

RFC 5988 explicitly forbids treating combinations of relation types differently from relation types that are present separately, so "collection" + "create" should not be treated differently from "create" or "collection" individually.

Good point. I don't think that would be an issue for "create", but it would for the "create-schema" concept.

One option is to document the usage of underspecified link relation types in a given API, but it's not clear to me how that fits with generic clients. It's not truly generic if you have to read non-standardized documentation.

Agreed. In fact I've found very few of the IANA registered relations to be usable outside of the specific context they were designed for. Even "edit", which seems like it should be fairly generic, references constructs defined in Atom and therefore isn't reusable outside of Atom. Clearly something more is needed. RDO is the best way I've been able to come up with to address this problem.

@handrews
Copy link
Contributor

handrews commented Mar 21, 2017

The fact that the behavior of targetSchema is coupled to the HTTP methods...

I don't think it is. "targetSchema" hints at the schema for the representation of the target resource. It is not a response schema. HTTP semantics require that all of the following be a representation of the target resource, and therefore (assuming the hint is correct) be valid against "targetSchema":

  • the response of a GET
  • the request of a PUT
  • the response to any request where the status is 200 and the "Content-Location" header is equal to the request URI

The semantics of PATCH define the request in terms of both the resource representation and the request media type, so there is an indirect relation between PATCH requests and "targetSchema"

Of course the representation of the target resource is irrelevant to a DELETE request, and with a POST request there is no guarantee of any relationship between the target resource representation and the request document. So "targetSchema" is irrelevant to those methods.

But all of this is defined by RFC 7231 (HTTP Semantics) and RFC 5789 (PATCH). It has nothing to do with JSON Hyper-Schema at all. "targetSchema" always means the same thing, but that thing applies differently to each HTTP method in a well-defined way.

RDO is the best way I've been able to come up with to address this problem.

I still find the RDO an interesting concept, I'm just not convinced that it belongs in the Hyper-Schema spec. I'm not 100% convinced it doesn't, either. Mostly I feel like I'm missing something about link relation types. Which is why I'm pestering mnot on mnot/I-D#211 :-)

@handrews
Copy link
Contributor

@jdesrosiers I just read through RFC 6573: "The Item and Collection Link Relations
"
and noticed that it lists application/vnd.collection+json and application/vnd.maze+xml as two examples of usage. Both of those specifications define that a "collection" link means that HTTP POST can be used to create a collection item.

So I think once Draft 06 is out the door I'll file a new issue for JSON Hyper-Schema to reference RFC 6573 and specify the semantics of those link relations in the context of application/schema+json. That seems to be both valid an useful. And would be enough information for a generic client to process correctly. An HTTP header with a rel of "collection" is not guaranteed to accept a POST to create, but we can specify that an API claiming a conforming use of Hyper-Schema MUST either do so or return a 405 Not Allowed for POST. Or maybe SHOULD. We can debate it after Draft 06 is out.

The general issue of overly broad link relations is still an issue, but it seems like this case seems easy enough to solve. This would unambiguously replace Draft 04's proposed "instances" and "create" link relations with existing IANA-registered link relations.

@Relequestual
Copy link
Member

After discussion elsewhere with @handrews and rereading this issue, the problem (for me at least) is the false assumption that I can use JSON Hyper Schema to document my non hypermedia compliant API.

I think what we need is a preface on how to determine if using JSON Hyper Schema is "right for my API", and how to identify (or define, I don't know) whay a hypermedia API looks like. Maybe explain a few different situations. It looks like draft-4 allowed the abuse of JSH to document non hypermeida APIs.

I'm still wrapping my head round what hypermeida IS and how it can be used.
I'm planning to collate a list of articles and documentation on JSON Schema in general, but also about hypermeida APIs and JSON Hyper Schema.

Given I gave a talk about JSON Schema and how to use it for a hypermedia API last week, and I'm still a bit confused about stuff, I think this should be our main address for draft-7.

@handrews
Copy link
Contributor

@Relequestual FWIW I think that we should also have keywords that are documentation-oriented. I don't think that static documentation and dynamic hypermedia are incompatible, they're just different things that need different support. For instance, #73 is a way to support static documentation, but it did not get momentum for Draft 06 (and see also mnot/I-D#211 for more discussion related to the topic of HTTP hints).

@jdesrosiers
Copy link
Member

@Relequestual, I don't think it's right to think about Hyper-Schema as documentation. I often refer to Hypermedia APIs as self documenting, but it's more about reducing the number of things that need documenting. It's kinda like if you referred to the method signatures in your classes as documentation. In a way they are, but they are also parseable, executable, and an integral part of the system.

@handrews
Copy link
Contributor

@Relequestual @jdesrosiers I'm part of the way through a big comparison of hypermedia vs API description which I'll put on the wiki. Or maybe our web site through the other repo. I just got distracted replying to #280 so it will have to wait until tomorrow :-)

@Relequestual
Copy link
Member

Great thanks @handrews this will be super useful. I think as a first pass it should go on the wiki with a view to probably migrate it to the website once us non hypermedia understanding luddites feel the comparison and explaianition is sufficent to make sense =]

@eviratec
Copy link

@Relequestual you might find my schemas useful...I'm currently adding/updating them daily...
https://github.com/eviratec/schema/tree/master/v1

Also see for examples of objects which are considered valid the schemas
https://github.com/eviratec/schema/tree/master/docs/v1

@Anthropic
Copy link
Collaborator

@handrews will you post an update here when done? I've always felt I didn't really understand the point of the hypermedia spec correctly coming from a different starting perspective.

@Relequestual
Copy link
Member

@eviratec Thanks but this is to do with @handrews explaining how JSON Hyper Schema should be used. I'm familiar with the majority of JSON Schema and validation already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants