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.
Related
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
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.
If I wanted to create (POST) a new resource linking two independent resources, what is the most proper - with respect to HATEOAS and REST principles - way to structure the entity of the request?
Any references in RFCs, W3C documents, Fielding's thesis, etc., about the proper way for a client to request two independent resources be linked together would be most valuable. Or, if what I'm interested in is simply outside the scope of REST, HATEOAS, an explanation of why would also be great.
Hopefully my question above is clear. If not, here's a scenario and some background to ground the question.
Let's say I have two independent resources: /customer and /item, and a third resource /order intended to the two.
If I'm representing these resource to the client in a HATEOAS-like way (say with JSON-LD), a customer might (minimally) look like:
{
"#id": "http://api.example.com/customer/1"
}
and similarly an item like:
{
"#id": "http://api.example.com/item/1"
}
I'm more concerned about what scheme the entity of the POST request should have, rather than the URL I'm addressing the request to. Assuming I'm addressing the request to /order, would POSTing the following run afoul of HATEOAS and REST principles in any way?
{
"customer": {"#id": "http://api.example.com/customer/1"},
"item": {"#id": "http://api.example.com/item/1"}
}
To me, this seems intuitively OK. However, I can't find much or any discussion of the right way to link two independent resources with a POST. I discovered the LINK and UNLINK HTTP methods, but these seem inappropriate for a public API.
The client does not build URIs, so this is wrong unless these resource identifiers or at least their template came from the service. It is okay to use the id numbers instead of the URIs until you describe this in the response which contains the POST link.
An example from the hydra documentation:
{
"#context": "http://www.w3.org/ns/hydra/context.jsonld",
"#id": "http://api.example.com/doc/#comments",
"#type": "Link",
"title": "Comments",
"description": "A link to comments with an operation to create a new comment.",
"supportedOperation": [
{
"#type": "CreateResourceOperation",
"title": "Creates a new comment",
"method": "POST",
"expects": "http://api.example.com/doc/#Comment",
"returns": "http://api.example.com/doc/#Comment",
"possibleStatus": [
... Statuses that should be expected and handled properly ...
]
}
]
}
The "http://api.example.com/doc/#Comment" contains the property descriptions.
{
"#context": "http://www.w3.org/ns/hydra/context.jsonld",
"#id": "http://api.example.com/doc/#Comment",
"#type": "Class",
"title": "The name of the class",
"description": "A short description of the class.",
"supportedProperty": [
... Properties known to be supported by the class ...
{
"#type": "SupportedProperty",
"property": "#property", // The property
"required": true, // Is the property required in a request to be valid?
"readable": false, // Can the client retrieve the property's value?
"writeable": true // Can the client change the property's value?
}
]
}
A supported property can have an rdfs:range, which describes the value constraints. This is not yet (2015.10.22.) added to the hydra vocab as far as I can tell, but I don't have time to follow the project. I think you still can use the rdfs:range instead of waiting for a hydra range.
So in your case you could add an item property with a range of http://api.example.com/doc/#Item and so on. I assume you could add the links of the alternatives, something like http://api.example.com/items/, so you could generate a select input box. Be aware that this technology is not stable yet.
So you can send a simple JSON as POST body {item: {id:1}, customer: {id:1}} or something like that, which you generate based on the POST link. The RDF is for the client not for the server. The server can understand the data structure it requires, it does not need RDF. You don't need a dictionary to understand yourself...
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.