I am in the early stages of planning a REST api, and I would like for it to adhere to the HATEOAS constraint of REST. But I would also like to provide a JSON format. So my question is if there are conventions out there to represent links and forms in JSON.
I have found examples of links, and it looks like this is a pretty common way of representing links:
"links": [
{"rel": "self", "href":"http://example.org/entity/1"},
{"rel": "friends", "href":"http://example.org/entity/1/friends"}]
Representing forms on the other hand, is not something that I have seen much of. I was thinking that perhaps somebody had sat down and thought up something along these lines, but considered all the caveats:
"forms" : [
{"rel" : "new client", "action" : "/clients", "method": "post",
"fields" : ["name":"string", "zipcode":"int", "signedup":"date", "state": ["Alabama",...]...]}]
The inspiration for this comes from looking at this video, where Jon Moore suggests that JSON is not a good format for a hypermedia api:
http://oredev.org/2010/sessions/hypermedia-apis
A really good talk by the way!
All input is appreciated!
REST architectural style lets us use the hypermedia links in the API response contents. It allows the client to dynamically navigate to the appropriate resources by traversing the hypermedia links.
A hypermedia API is an API that returns hypermedia, typically HTML over HTTP. This style of API is distinguished from data APIs that do not return a hypermedia. The most familiar form of this latter style of API today is the ubiquitous JSON API.
HATEOAS, or Hypermedia as the Engine of Application State, is a complicated-sounding term for a simple idea: A client interacts with a REST API entirely through the responses provided dynamically by the server. Put even more simply: You shouldn't need any documentation or out-of-band information to use a REST API.
An hypermedia format defines the contract between client and server. It's the hyperlink-enabled data format you are using for a particular representation of a resource in an hypermedia application.
I have investigated this topic for a while, but I am not certain about which possible solutions ppl use and which not. There are only a few examples available... So I'll need some review from experts... (My examples will be mostly in HAL+JSON.)
1.)
I have a feeling that link relations should be GET only, because in HTML they are for including things like stylesheets. I guess other ppl had the same feeling, because there is an edit-form
and a create-form
by IANA link relations.
So the first possible solution to dereference links with form relations and so download the form descriptions for the write operations. These form descriptions can contains HTML fragments or a schema which we can use to generate the forms. For example
Just to mention you can use the same approach by sending the same links in the header and send raw JSON as body.
{
"_links": {
"edit-form": {
"href": "http://example.com/users/1?form=edit",
"type": "text/html",
"title": "Edit user"
}
}
}
So what if link relations are not just for read purposes?
2.)
Then we can use the built-in features of HAL:
If we send data, then we can use the type
to describe the request body instead of the response body. Ofc. in this case there should not be a response body, or this solution will be confusing.
{
"_links": {
"curies": [
{
"name": "my",
"href": "http://example.com/rels/{rel}",
"templated": true
}
],
"my:edit": {
"href": "http://example.com/users/1",
"type": "application/vnd.example.user+json",
"title": "Edit user"
}
}
}
So in this case the client will know that my:edit
means that this is an edit form, and by checking the MIME type it will know what type of form to display.
An alternative solution to use the custom link relation for the same purpose:
{
"_links": {
"curies": [
{
"name": "my",
"href": "http://example.com/rels/{rel}",
"templated": true
}
],
"my:edit-user": {
"href": "http://example.com/users/1",
"type": "application/json",
"title": "Edit user"
}
}
}
So by fetching the docs http://example.com/rels/edit-user
we can find a description about how to build a form for editing users and so we can support the my:edit-user
link relation in our client. The docs can contain optionally a HTML form or some schema, or an RDF document using a form description vocab, etc...
We can follow the same approach by the profile
property of the links. For example:
{
"_links": {
"curies": [
{
"name": "my",
"href": "http://example.com/rels/{rel}",
"templated": true
}
],
"my:edit": {
"href": "http://example.com/users/1",
"type": "application/json",
"title": "Edit user",
"profile": "http://example.com/profiles/user"
}
}
}
So in here the link relation means that this is an edit form and the profile
describes how to generate the form under the http://example.com/profiles/user
URL.
3.)
Or we can extend HAL using custom properties.
For example dougrain-forms does this:
{
"_forms": {
"edit": {
"href": "http://example.com/users/1",
"headers": {
"content-type": "application/json"
},
"title": "Edit user",
"method": "PUT",
"schema": {
"required": [
"name"
],
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"title": "user properties"
}
}
}
}
But you can use any alternative approach as long as we don't have a standard about HAL and about HAL forms, for example I would rather use a mongoose schema like solution:
{
"name": "John",
"_links": {
"curies": [
{
"name": "my",
"href": "http://example.com/rels/{rel}",
"templated": true
}
],
"my:edit": {
"href": "http://example.com/users/1",
"type": "application/json",
"title": "Edit user",
"method": "PUT",
"_embedded": {
"schema": {
"name": "String"
}
}
}
}
}
4.)
Don't use link relations and simple JSON formats like HAL, use RDF with one or more vocabulary instead. It is harder to use RDF, but it is a fine grained solution for decoupling clients from REST services, while HAL is just a coarse grained solution...
For example JSON-LD with Hydra and a custom vocab:
{
"@context": [
"http://www.w3.org/ns/hydra/core",
"https://example.com/docs#"
],
"@id": "https://example.com/users/1",
"name": "John",
"operation": {
"@type": "ReplaceResourceOperation",
"title": "Edit user",
"method": "PUT",
"expects": {
"@id": "https://example.com/docs#User",
"supportedProperty": {
"@type": "SupportedProperty",
"title": "name",
"property": "https://example.com/docs#User.name",
"range": "http://www.w3.org/2001/XMLSchema#string",
"required": true
}
}
}
}
The JSON Schema standard (particularly "hyper-schemas") definitely allows this. You reference a JSON (Hyper-)Schema (using HTTP headers) and the schema defines rules on how to interpret your data as hyper-text.
The information for constructing your links can be anywhere. The hyper-schema documents how to assemble link URIs from the data (it can be a template), and they also specify HTTP method, encoding type, and so on.
To get form functionality: you can specify a full schema for the data to be submitted along with the request. Required/optional properties, array length constraints, whatever.
As a demo, here's part of a walkthrough for a JavaScript library that understands hyper-schemas and can present an appropriate form for links: jsonary.com.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With