We intend to use Spring-HATEOAS to enrich our interface with hypermedia informations via HAL/JSON.
What we are wondering is, how to provide sufficient meta information of what we are going to find in a resource after following a link.
I identified different methods to publish such information, with one being the content type of the resource and the other being a profile.
However both do not allow any kind of polymorphism.
Let's assume we model a weather station which has a temperature a wind and a light sensor.
In my concept I would link those three sensors:
"item" : [
{ "href" : ".../sensors/1" } // temperature
{ "href" : ".../sensors/2" } // wind
{ "href" : ".../sensors/3" } // light
]
which means they are part of my sensor collection (which my weather station is).
To the user of my weather station, I would like to provide the meta information, that:
All three sensors are sensors (which implys the existence of certain properties)
Sensor 1 is a unidirectional sensor, whil 2 and 3 only measure for a specific direction
Sensor 3 provides addtional to the value (intensity) some spectral information.
So in Code:
class TemperatureSensor extends Sensor
class WindSensor extends Sensor implements DirectionalSensor
class LightSensor extends Sensor implements DirectionalSensor, SprectralSensor
How can I provide those information to the user, using Spring-HATEOAS or directly HAL?
I identified different methods to publish such information, with one being the content type of the resource and the other being a profile.
In general the media type defines how to process a payload but not necessarily what object or type its content relates to. I.e. on receiving a HTML payload you don't necessarily know that the page contains user information or the like, unless you have certain semantic annotations present within the markup. All HTML defines is a set of valid elements, how these elements have to be embedded in the payload (i.e. either <element>...</element> or <element/>), which attributes they support and when it is admissible to add which elements, i.e. certain elements such as the list-item tag <li> makes only sense as part of an unordered list <ul> or its counter-part the order list <ol>.
In regards to profiles, according to RFC 6906
A profile is defined not to alter the semantics of the resource representation itself, but to allow clients to learn about additional semantics (constraints, conventions, extensions) that are associated with the resource representation, in addition to those defined by the media type and possibly other mechanisms.
It is therefore a configuration option to set on the media-type processor, which depending on the profile specified, might apply additional validation rules, allow certain elements to appear in certain elements or the like. I.e. HTML4.01 added profiles to the <head> element so that search engines that understand this profile know that meta-information for author, date, keyword and copyright will be present which they can use directly instead of attempting to parse that information from the body directly.
HAL supports both the specification of profiles on media-type definitions as well as on link objects.
... how to provide sufficient meta information of what we are going to find in a resource after following a link.
In HTML a user is usually hinted what invoking a link might return by adding additional text, that summarizes the content of that target, or images, that express an affordance to the user, to the link context. For humans this is usually easy to understand though for an automated process such meta information are usually difficult to process and act upon. Instead of using free-text or images to express the relation the target has to the current content, link relations are used to express this.
According to RFC 8288 (Web Linking)
... an application will define the link relation type(s) it uses, along with the serialisation(s) that they might occur within. For example, the application "Web browsing" looks for the "stylesheet" link relation type in the HTML link serialisation (and optionally in the Link header field), whereas the application "AtomPub" uses the "edit" and "edit-media" link relations in the Atom serialisation.
Web linking also describes that link-relations not only describe simple semantics but also particular attributes or behaviors. More formally, they describe how the current context is related to an other resource.
Wikipedia describes link relations as:
A link relation is a descriptive attribute attached to a hyperlink in order to define the type of the link, or the relationship between the source and destination resources. The attribute can be used by automated systems, or can be presented to a user in a different way.
Such link relations should be based on standardized terms or make use of an extension mechanism, i.e. dublin-core. Microformats also lists plenty of commonly used relation names in HTML5. While link-relations must not constrain the processing of the current document or the availability of target representation types, they can specify certain behaviors or properties of target resources, i.e. that a resource supports certain HTTP methods or that support of certain media-type formats is required.
A link may have multiple different link relation names assigned. Clients that do not understand a certain link relation name should ignore it and only operate on those they do know and support. This basically just allows to add as many relation names to the URI context as needed. This is similar to the semantic Web where there may exist multiple predicates between a subject and object and further relation exist that indicate that a predicate expresses the same as an other one and may thus be used interchangingly.
HAL supports link-relations out of the box and adds CURIEs on top, which is a further reserved link-relation name itself, that hints a client on the location of a resource documentation. Link relation extension, as defined by RFC 8288, do not necessarily need to point to a documentation describing the semantics, therefore clients shouldn't access such URIs by default.
A links-section within a HAL representation response may look like this for the given problem statement:
...
"links": {
"self": { "href": "/weatherstation" },
"curies": [{ "name": "ws", "href": "http://api.weatherstation.com/docs/rels/{rel}", "templated": true }],
"ws:sensors": [
{ "href": "../sensors/1", "title": "temperature" },
{ "href": "../sensors/2", "title": "wind" },
{ "href": "../sensors/3", "title": "light" }
],
"ws:unidirectional": { "href": "../sensors/1", "title": "temperature" },
"ws:directional": [
{ "href": "../sensors/2", "title": "wind" },
{ "href": "../sensors/3", "title": "light" }
],
"ws:spectral": { "href": "../sensors/3", "title": "light" },
...
"http://api.weatherstation.com/rel/sensors": [
{ "href": "../sensors/1" },
{ "href": "../sensors/2" },
{ "href": "../sensors/3" }
],
"http://api.weatherstation.com/rel/unidirectional": { "href": "../sensors/1" },
"http://api.weatherstation.com/rel/directional": [
{ "href": "../sensors/2" },
{ "href": "../sensors/3" }
],
"http://api.weatherstation.com/rel/spectral": { "href": "../sensors/3" }
}
At this point I'm not 100% sure whether Curies also express link-relations or just express the documentation of a resource, hence I divided the sample above a bit. In theory they should be able to be valid link-relation names itself, in which case the latter definition may be skipped, as the HAL processor will resolve them to a full URI as required by RFC 8288 anyway.
While Web linking would allow for a link-relation such as:
Link: <../sensors/3>; rel="http://api.weatherstation.com/rel/sensors http://api.weatherstation.com/rel/directional http://api.weatherstation.com/rel/spectral"
that defines all 3 attributes on the same URI, I'm not sure if this is also possible in HAL directly.
Support for Curies is documented in the reference documentation where you basically just have to add a CurieProvider bean to your config. This Curie provider kicks in on all non registered link relations you define via RelProvider. Registered link relations can easily be added via new Link("/some-target", IanaLinkRelations.NEXT) for example as documented here
Related
I have the following data structure that contains an array of sectionIds. They are stored in the order in which they were completed:
applicationProgress: ["sectionG", "sectionZ", "sectionA"]
I’d like to be able to do something like:
GET /application-progress - expected: sectionG, sectionZ, sectionA
GET /application-progress?filter[first]=true - expected: sectionG
GET /application-progress?filter[current]=true - expected: sectionA
GET /application-progress?filter[previous]=sectionZ - expected: sectionG
I appreciated the above URLs are incorrect, but I’m not sure how to name/structure them to get the expected data e.g. Are the resources here "sectionids"?
I'd like to adhere to the JSON:API specification.
UPDATE
I'm looking to adhere to JSON:API v1.0
In terms of resources I believe I have "Section" and "ProgressEntry". Each ProgressEntry will have a one-to-one relationship with a Section.
I'd like to be able to query within the collection e.g.
Get the first item in the collection:
GET /progress-entries?filter[first]
Returns:
{
"data": {
"type": "progress-entries",
"id": "progressL",
"attributes": {
"sectionId": "sectionG"
},
"relationships": {
"section": {
"links": {
"related": "http://example.com/sections/sectionG"
}
}
}
},
"included": [
{
"links": {
"self": "http://example.com/sections/sectionG"
},
"type": "sections",
"id": "sectionG",
"attributes": {
"id": "sectionG",
"title": "Some title"
}
}
]
}
Get the previous ProgressEntry given a relative ProgressEntry. So in the following example find a ProgressEntry whose sectionId attribute equals "sectionZ" and then get the previous entry (sectionG). I wasn't clear before that the filtering of this is based on the ProgressEntry's attributes:
GET /progress-entries?filter[attributes][sectionId]=sectionZ&filterAction=getPreviousEntry
Returns:
{
"data": {
"type": "progress-entries",
"id": "progressL",
"attributes": {
"sectionId": "sectionG"
},
"relationships": {
"section": {
"links": {
"related": "http://example.com/sections/sectionG"
}
}
}
},
"included": [
{
"links": {
"self": "http://example.com/sections/sectionG"
},
"type": "sections",
"id": "sectionG",
"attributes": {
"id": "sectionG",
"title": "Some title"
}
}
]
}
I started to comment on jelhan's reply though my answer was just to long for a reasonable comment on his objection, hence I include it here as it more or less provides a good introduction into the answer anyways.
A resource is identified by a unique identifier (URI). A URI is in general independent from any representation format else content-type negotiation would be useless. json-api is a media-type that defines the structure and semantics of representations exchanged for a specific resource. A media-type SHOULD NOT force any constraints on the URI structure of a resource as it is independent from it. One can't deduce the media-type to use based on a given URI even if the URI contains something like vnd.api+json as this might just be a Web page talking about json:api. A client may as well request application/hal+json instead of application/vnd.api+json on the same URI and receive the same state information just packaged in a different representation syntax, if the server supports both representation formats.
Profiles, as mentioned by jelhan, are just extension mechanisms to the actual media-type that allow a general media-type to specialize through adding further constraints, conventions or extensions. Such profiles use URIs similar to XML namespaces, and those URIs NEED NOT but SHOULD BE de-referencable to allow access to further documentation. There is no talk about the URI of the actual resource other than given by Web Linking that URIs may hint a client on the media-type to use, which I would not recommend as this requires a client to have certain knowledge about that hint.
As mentioned in my initial comments, URIs shouldn't convey semantics as link relations are there for!
Link-relations
By that, your outlined resource seems to be a collection of some further resources, sections by your domain language. While pagination as defined in json:api does not directly map here perfectly, unless you have so many sections that you want to split these into multiple pages, the same concept can be used using standardized link relations defined by IANA.
Here, at one point a server may provide you a link to the collection resource which may look like this:
{
"links": {
"self": "https://api.acme.org/section-queue",
"collection": "https://api.acme.org/app-progression",
...
},
...
}
Due to the collection link relation standardized by IANA you know that this resource may hold a collection of entries which upon invoking may return a json:api representation such as:
{
"links": {
"self": "https://api.acme.org/app-progression",
"first": "https://api.acme.org/app-progression/sectionG",
"last": "https://api/acme.org/app-progression/sectionA",
"current": "https://api.acme.org/app-progression",
"up": "https://api.acme.org/section-queue",
"https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionG",
"https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionZ",
"https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionA",
...
},
...
}
where you have further links to go up or down the hierarchy or select the first or last section that finished. Note the last 3 sample URIs that leverages the extension relation types mechanism defined by RFC 5988 (Web Linking).
On drilling down the hierarchy further you might find links such as
{
"links": {
"self": "https://api.acme.org/app-progression/sectionZ",
"first": "https://api.acme.org/app-progression/sectionG",
"prev": "https://api.acme.org/app-progression/sectionG",
"next": "https://api.acme.org/app-progression/sectionA",
"last": "https://api.acme.org/app-progression/sectionA",
"current": "https://api.acme.org/app-progression/sectionA",
"up": "https://api.acme.org/app-progression",
...
},
...
}
This example should just showcase how a server is providing you with all the options a client may need to progress through its task. It will simply follow the links it is interested in. Based on the link relation names provided a client can make informed choices on whether the provided link is of interest or not. If it i.e. knows that a resource is a collection it might to traverse through all the elements and processes them one by one (or by multiple threads concurrently).
This approach is quite common on the Internet and allows the server to easily change its URI scheme over time as clients will only act upon the link relation name and only invoke the URI without attempting to deduce any logic from it. This technique is also easily usable for other media-types such as application/hal+json or the like and allows each of the respective resources to be cached and reused by default, which might take away load from your server, depending on the amount of queries it has to deal with.
Note that no word on the actual content of that section was yet said. It might be a complex summary of things typical to sections or it might just be a word. We could classify it and give it a name, as such even a simple word is a valid target for a resource. Further, as Jim Webber mentioned, your resources you expose via HTTP (REST) and your domain model are not identical and usually do not map one-to-one.
Filtering
json:api allows to group parameters together semantically by defining a customized x-www-form-urlencoded parsing. If content-type negotiation is used to agree on json:api as representation format, the likelihood of interoperability issues is rather low, though if such a representation is sent unsolicitedly parsing of such query parameters might fail.
It is important to mention that in a REST architecture clients should only use links provided by the server and not generate one on their own. A client usually is not interested in the URI but in the content of the URI, hence the server needs to know how to act upon the URI.
The outlined suggestions can be used but also URIs of the form
.../application-progress?filter=first
.../application-progress?filter=current
.../application-progress?filter=previous&on=sectionZ
can be used instead. This approach should in addition to that also work on almost all clients without the need to change their url-encoded parsing mechanism. In addition to that he management overhead to return URIs for other media-types generated may be minimized as well. Note that each of the URIs in the example above represent their own resource and a cache will store responses to such resources based on the URI used to retrieve such results. Queries like .../application-progress?filter=next&on=sectionG and .../application-progress?filter=previous&on=sectionA which retrieve basically the same representations are two distinctive resources which will be processed two times by your API as the response of the first query can't be reused as the cache key (URI) is different. According to Fielding caching is one of the few constraints REST has which has to be respected otherwise you are violating it.
How you design such URIs is completely up to you here. The important thing is, how you teach a client when to invoke such URIs and when it should not. Here, again, link-relations can and should be used.
Summary
In summary, which approach you prefer is up to you as well as which URI style you choose. Clients, especially in a REST environment, do not care about the structure of the URI. They operate on link-relations and use the URI just for invoking it to progress on with their task. As such, a server API should help a client by teaching it what it needs to know like in a text-based computer game in the 70/80's as mentioned by Jim Webber. It is helpful to think of the interaction model to design as affordances and state machine as explained by Asbjørn Ulsberg .
While you could apply filtering on grouped parameters provided by json:api such links may only be usable within the `json:api´ representation. If you copy & paste such a link to a browser or to some other channel, it might not be processable by that client. Therefore this would not be my first choice, TBH. Whether or not you design sections to be their own resource or just properties you want to retrieve is your choice here as well. We don't know really what sections are in your domain model, IMO it sounds like a valid resource though that may or may not have further properties.
Suppose I have a RESTful HATEOAS API which has /posts endpoint which lists posts with a query shortcut /posts/new. How do I query the API to discover /posts/new?
My ideas:
1) Query /posts and get links from _links attribute (and the entities listed are necessary overhead):
GET /posts
{
"docs": [
...
]
"_links": {
"new": { "rel": "posts", "href": "/posts/new" }
}
}
2) Provide this in the API root together with list of resources:
GET /
{
"resources": {
"posts": {
"_links": {
"self": { "rel": "posts", "href": "/posts" }
"new": { "rel": "posts", "href": "/posts/new" }
}
}
}
}
3) I should not use the /posts/new query and instead use /posts and query params. However, if I change my server logic I would have to change client logic too and that would be serve-client coupling. For example:
New messages will be requested by client by somehow providing parameter timestamp > (today - 30)
I introduce draft property and change my idea that new are only the posts with timestamp > (today - 30) && draft = false
I have to change client to add drafts constraint
Note: posts is just an example I am asking in general.
In a REST architecture URIs should be discovered via their accompanying link-relation name. On interpreting your examples above as HAL the URI /post/new has a link-relation name of new. Link relation names provide semantics to URIs which allow clients to determine when to invoke these URIs. HAL is just one of a handful JSON-based media types that support HATEOAS. There are further media-types available that provide a similar job with slightly different syntax and capabilities.
Upon receiving such a document a client would parse the message and build some context for the message containing the actual content including additional metadata like links and further embedded data. If it wants to retrieve the list of the most recent posts it basically needs to look up the key (link-relation name) that expresses the intent (new in your case) from the before-mentioned context in order to retrieve the assigned value (URI). How a client maintains this context is some implementation detail. It might build up a tree-map for easier lookup of "link-relation" keys and their values (URIs) or use some totally different approach.
The knowledge what key to use needs to be present somehow. As link relations express certain semantics they need to be specified somewhere. This can happen in industry standards or media-type definitions. IANA maintains a list of standardized link-relation names and their semantics. On examining the list probably the most likely match according to your specification is current which is defined as
Refers to a resource containing the most recent item(s) in a collection of resources.
I'd therefore propose to change the link-relation name from new to current.
Well, the whole point of the RESTFUL is to make the links discovery easy by making them correspond to the HTTP method used by the client. That means all your links would be simply named /post, the only thing that would change is the htpp method and the parameters they take, which your server would use to determine the actual operation the client wants.
This is a sample from a C# project (notice that the links are all the same, the only changes are the HTTP_METHOD and/or the parameter passed):
List of common http methods: POST, GET, PUT, DELETE
I understand HATEOAS represents the applications state by sending all actions that can be performed at that point in time within the application as it's response (HAL, JSON-LD, etc).
For example, viewing an account resource of a bank may allow you to deposit, withdraw or close the account (OPTIONS which may return UPDATE and DELETE verbs).
In terms of runtime discoverability of these links (by the consuming client), how might one go about this?
If the purpose of sending these links is the decouple the client from the server and drive the state by the hypermedia in the response, there must be an amount of knowledge the developer must hardcode in the application in order to make any sense of the responses being returned.
I understanding sending OPTIONS requests is the way to determine the current state of the resource and what you can do next, but in order to discover the actual URIs to use - would these simply be hardcoded as COOL URIs?
Like #VoicesOfUnreason said, in HATEOAS URIs are discoverable (and not documented) so that they can be changed. That is, unless they are the very entry points into your system (Cool URIs, the only ones that can be hard-coded by clients) - and you shouldn't have too many of those if you want the ability to evolve the rest of your system's URI structure in the future. This is in fact one of the most useful features of REST.
For the remaining non-Cool URIs, they can be changed over time, and your API documentation should spell out the fact that they should be discovered at runtime through hypermedia traversal.
Looking at the Richardson's Maturity Model (level 3), this would be where links come into play. For example, from the top level, say /api/version(/1), you would discover there's a link to the groups. Here's how this could look in a tool like HAL Browser:
Root:
{
"_links": {
"self": {
"href": "/api/root"
},
"api:group-add": {
"href": "http://apiname:port/api/group"
},
"api:group-search": {
"href": "http://apiname:port/api/group?pageNumber={pageNumber}&pageSize={pageSize}&sort={sort}"
},
"api:group-by-id": {
"href": "http://apiname:port/api/group/id" (OR "href": "http://apiname:port/api/group?id={id}")
}
}
}
The add would simply be a POST to that endpoint, and then you'd have 2 GET methods.
GET /api/group?pageNumber=0&pageSize=20&sort=asc
which could return something like this:
{
"groups": [
{
"id": 123,
"name": "Test Group"
},
{
"id": 134,
"name": "Tennis squad"
}
]
}
Then once you drill down to a particular group (say #123):
{
"Id" : 123,
"Name" : "test",
"_links": {
"self": {
"href": "/api/group/1" (OR "/api/group?id=1")
},
"edit": {
"href": "http://apiname:port/api/group/1"
},
"api:delete": {
"href": "http://apiname:port/api/group/1"
},
"api:items-query": {
"href": "http://apiname:port/api/bonus?groupId=1"
}
}
}
Here, the edit would simply be a PUT, and then you'll need a DELETE (see level 2 of REST in that same link), as for the items, you probably know best if they are just a property, or another endpoint; you could even embed them to be returned in the same call that's retrieving a group.
The advantage here would be that the client would only need to know the relationship (link) name (well obviously besides the resource structure/properties), while the server would be mostly free to alter the relationship (and resource) url.
There's a bunch of prior art around on trying to create expressive, discoverable hypermedia. You might want to review:
http://json-ld.org/
http://www.markus-lanthaler.com/hydra/
I am thinking maybe a series of if statement that checks for certain properties to determine the state or maybe even switch statements. Is this is correct path - or is there better means of hypermedia discovery?
My current thinking is that you want to be shaping your ideas more along the lines of negotiating and following a protocol; so think state machine rather than if statements.
For starters, review How To GET a Cup of Coffee.
The hyperlinks in the documents served by RESTBucks are designed to assist the client in negotiating the RESTBucks protocol; the assumption that the client already understands that protocol is baked into the model. In other words, the client already understands that negotiating the protocol will allow it to reach it's goal.
Of course, there could be multiple protocols that serve the same goal. For instance RESTBucks could also support a "Give Away Day Old Coffee" protocol; announcing the presence of each, the client would be expected to choose which is the better expression of the goal, and follow that path.
On forhand : sorry if I misunderstood hypermedia or Restfull concepts : it's a work in progress...)
I try to figure out hypermedia and hydra (http://www.markus-lanthaler.com/hydra), and have some questions about returning information to the client before designing my api.
say I have a webshop located at www.myshop.com
a HTTP GET to the root could return (for example) a list of resources represented as link (in a json-ld document):
...
"#id": "/api",
"products" : "www.myshop.com/api/products",
"customers":"www.myshop.com/api/customers"
...
First question on hydra, how could I add actions here ? it seems the client needs to load another document before the load of application. I mean the potential actions are not in the docuemnt retrieved from www.myshop.com/api Or do I miss something?
Then going further, I've stated that products is a hydra:Link so that the client could follow that link (interact with it) with a HTTP GET and retrieve a list of products. that will be a list like this :
....
{
"#id": "/api/products/123",
"#type": "vocab:Product"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product"
},
....
here the client receives a list of product (That could be a paged collection). But if the client wants to display it to the user, let's say a table with [product Id, price, name] (not all Product's properties)
Second Question : How could I do that without the client sending a request to the server for each product, but still provide the link to get the product's detailed information,(or even here having four link : one for getting the detailed information, one for Delete and one for sharing it with a friend and a last one to add it to a Basket) ?
In fact I have difficulties to figure out how hydra is coming into play by not having Links in the document itself? I think that Hal uses this approach to having links in the document itself (if I am right) and I try to find how hydra does this link...
regards
A bit late but I'll nevertheless try to answer your questions Cedric.
say I have a webshop located at www.myshop.com
a HTTP GET to the root could return (for example) a list of resources
represented as link (in a json-ld document):
... "#id": "/api",
"products" : "www.myshop.com/api/products",
"customers":"www.myshop.com/api/customers" ...
First question on hydra, how could I add actions here ? it seems the
client needs to load another document before the load of application.
I mean the potential actions are not in the docuemnt retrieved from
www.myshop.com/api Or do I miss something?
You basically have two options here: 1) embed the operations directly in the response or 2) attach the operations to the properties (products, customers) instead.
Approach 1) would look somewhat like this:
...
"#id": "/api",
"products" : {
"#id": "http://www.myshop.com/api/products",
"operation": {
"#type": "Operation",
"method": "POST",
"expects": "Product"
}
}
...
While approach 2) would attach the same operation to the products property in the referenced Hydra ApiDocumentation:
...
"#id": "...products",
"supportedOperation": {
"#type": "Operation",
"method": "POST",
"expects": "Product"
}
...
Please note that in 1) I used operation while in 2) I used supportedOperation. Also, you should use a more specific type than Operation.
Regarding your second question:
with a HTTP GET and retrieve a list of products. that will be a list like this :
....
{
"#id": "/api/products/123",
"#type": "vocab:Product"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product"
},
....
here the client receives a list of product (That could be a paged
collection). But if the client wants to display it to the user, let's
say a table with [product Id, price, name] (not all Product's
properties)
Second Question: How could I do that without the client sending a
request to the server for each product, but still provide the link to
get the product's detailed information,(or even here having four link
: one for getting the detailed information, one for Delete and one for
sharing it with a friend and a last one to add it to a Basket) ?
You can add as much information (including links) as you want directly in the collection.
....
{
"#id": "/api/products/123",
"#type": "vocab:Product",
"name": "Product 123",
"price": "9.99"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product",
"name": "Product 124",
"price": "19.99"
},
....
That way, a client only needs to dereference an item if the collection doesn't contain that required information.
In fact I have difficulties to figure out how hydra is coming into
play by not having Links in the document itself?
Of course you do have links in the document as well. Links are just properties whose values happen to be URLs (objects with an #id property unless you set the property's type to #id in the context to get rid of that) instead of treating them specially.
note: The Hydra part of the answers I am not so sure, the JSON-LD and REST are okay I think.
You can use #base and relative IRIs by JSON-LD, or you can define namespaces in the #context, so after that you can use relative IRIs as ns:relativeIRI. Each one is better than returning the full IRI. (It is easier to parse the results with a general JSON-LD parser on client side, instead of a simple JSON parser.)
You can define your own #vocab using the Hydra vocab, or you can add "action" definitions in the #context. If you want to "add actions" you have to use hydra:Operation sub-classes in your vocab. Something like this (but I am not a Hydra expert):
{
"#id": "vocab:ProductList",
//...
"hydra:supportedOperations": [
{
"#type": "hydra:CreateResourceOperation",
"method": "POST",
"expects": "vocab:Product"
}
//...
]
}
In general by REST, if you need the same resource with fewer properties, then you have to add a new IRI for that resource, e.g.: /myresource?fewer=1. For example in your case: /api/products/?fields="id, price, name" is okay.
By Hydra you have 2 choices if you want multiple links; you can add a new hydra:Link as a property, or you can add a new hydra:Operation as a supportedOperation with method: GET. I guess get operations are for something like search which has an user input, but if you don't want to add a new property for each link, I think you have no other option.
Actually Hydra does have link and operation support. Maybe it is not clear, but JSON-LD is an RDF format, you can define RDF triples in that. So the IRIs you used for example by "customers":"www.myshop.com/api/customers" are just resource identifiers and not links. A link should have IRI, title, method(GET), language, content-type, iana:relation, etc... so it is not possible to describe a link you can follow with just a single IRI (resource identifier). By processing a REST resource a client should never check the IRI structure to know how to display what it got from you. You have to check the other properties of the links, especially iana:relations or by Hydra maybe operation type to do that. So for example in your case www.myshop.com/api/dav8ufg723udvbquacvd723fudvg is a perfectly valid IRI for the list of the customers. We use nice IRIs only because it is easier to configure generate them on server side, and configure a router for them.
Please check the Hydra vocab before further questions. As you can see a Class can have supportedOperations and supportedProperties which are both collections. A Link is a Property sub-class which can have a single Operation. By collections I think you have to use the Collection class, in which member contains the items of the collection... Be aware that by JSON-LD there is no difference by defining a single item or multiple items with the same type. In the context you have to define only the type, and the value of the property can contain both a single item or an array of items... If you want some constraints about that I guess you have to add some OWL triples, and a validator which checks the values using them.
I'm interested in building a web service with a REST API. I've been reading about HATEOAS and many of the examples explain the concept by comparing it to what humans do when they surf the web. This has me thinking, why not build the REST API in such a way that it can be easily used by both humans and machines?
For example, I have an internal model of a widget, and this widget has properties like part number, price, etc. When a machine asks for a list of widgets, I can return a JSON representation.
{
widgets: [
{
id: 1,
part_number: "FOO123",
price: 100,
url: "/widget/1"
},
{
id: 2,
part_number: "FOO456",
price: 150,
url: "/widget/2"
},
{
id: 3,
part_number: "FOO789",
price: 200,
url: "/widget/3"
},
...
]
}
When a human requests the same list through his/her web browser, it seems like I should be able to take the same internal model and apply a different view to it to generate an HTML response. (Of course, I would decorate the HTML response with other page elements, like a header, footer, etc.)
Is this a sensible design? Why or why not? Are there any popular sites actually doing it?
The biggest drawback that I see is there is no obvious way for a user to delete a resource. In my use case, I'm not going to let users modify or delete resources, so this is not a deal-breaker, but in general how might you handle that?
#mehaase
First of all i'd suggest to use one of registered JSON hypermedia formats:
Collection+JSON: http://amundsen.com/media-types/collection/format/
Collection.next+JSON:
http://code.ge/media-types/collection-next-json/
HAL - Hypertext Application Language:
http://stateless.co/hal_specification.html
All of them offer explicit semantics for creating links with semantic link relations.
For example with Collection(.next)+JSON you can express your widgets like this:
{"collection": {
"version": 1.0,
"items": [{
"href": "/widget/1",
"data": [{
"name": "id",
"value": 1,
"prompt": "ID"
}, {
"name": "part_number",
"value": "FOO123",
"prompt": "Part number"
}, {
"name": "price",
"value": 100,
"prompt": "Price"
}],
"links": [{
"rel": "self",
"href": "http://...",
}, {
"rel": "edit",
"href": "http://..."
}]
}]
}}
This gives you several advantages:
You do not need to reinvent the wheel for specifying links
You can freely use all registered link relation types:
http://www.iana.org/assignments/link-relations/link-relations.xml
Based on your data structure, you can easily use collection/item semantics of mentioned format
If need be you can describe input forms as well
As you see from example, it has enough information for transforming to HTML(or other formats).
The biggest drawback that I see is there is no obvious way for a user
to delete a resource. In my use case, I'm not going to let users
modify or delete resources, so this is not a deal-breaker, but in
general how might you handle that?
for this read "edit" link relation specification, it implies that resource can be deleted.
There are a couple of things you can do, but the first premise is simply that the modern "generic" web browser is really crummy REST client.
If most of your interaction is guarded and managed by JavaScript, if you write a "rich client" so to speak where you're relying more on JS generated requests than simply links, forms, and the back button, then it can be a better REST client.
If you're stuck with the generic browser experience of forms and links, you can route around the lack of the other HTTP verbs by overloading POST. You lose some guarantees by intermediaries. DELETE is idempotent, POST is not, this has repercussions, but it's not devastating, and you just have to work around it. You can do idempotent things with POST, but intermediaries won't "know" that they are, so they can't assume its idempotent.
If you end up having to go "POST uber alles" you will either restrict your machine clients to the same api, or you offer up parallel services -- those used by POST stupid clients, and those others that have the full gamut available to them.
That said, if you choose an XML based hypermedia format, then what you can do is add XSL transforms to the XML payloads. The browsers will run the XSL on the payloads creating as pretty a page as you like (headers, footers, enough JS to choke a horse, etc.), while machines will ignore that aspect of it and focus solely on data as given.
Gives you a "best of both worlds" in that respect.
You can always build a REST API and then build your own, human-friendly web app around it. This is a common practice because you have out-of-the-box functionality and an extendable system for developers.
It is possible to do so simply by using HTML with RDFa. So humans can read the HTML and machines can read the RDFa annotations. You need a REST vocab like hydra to annotate the links, and other vocabs, like schema.org to annotate the content.
Another option to use different media types for different type of clients, like HTML for humans and JSON-LD for machines.