In contrast to Nicholas Shanks I think a wizard-like system is in line with the idea behind REST. It is not that uncommon to see such an interaction concept on the Web, i.e. usually applied on checkout pages where on one page you enter customer information, on the next page the delivery address and on a third page enter your payment data followed up by a confirmation page that on confirmation will trigger the actual order.
Jim Webber pointed out that in a REST architecture you primarily implement a domain application protocol (a state machine a client will traverse through, if you will) that client will follow along as they get all the information served by the server, either through links or form-like representation similar to HTML forms. This concept is summarized as HATEOAS.
So, an above-mentioned checkout system in a REST architecture might look like this: After putting an item into your basket the server offers you additional links that are annotated with some link relations (Pseudo-HAL representation):
{
...,
"_links": {
"self": {
"href": "https://..."
},
"create-form": {
"href": "https://shop.acme.com/checkout-wizard-p1"
},
"https://acme.com/rel/checkout": {
"href": "https://shop.acme.com/checkout-wizard-p1"
},
...
}
}
The URI itself isn't the relevant thing here as the spelling of an URI is not of importance in a REST architecture as long as it conforms to the rules outlined in RFC 3986 (URI). It could also end up with a UUID instead of the checkout-wizard-p1
name given here for simplicity. Instead, focus is on the link-relation names given here, i.e. create-form
, which conforms to the standardized link-relation registered at IANA and a custom link relation, https://acme.com/rel/checkout
, that follows the extension mechanism outlined by Web Linking (RFC 5988). Introducing this indirection allows servers to replace the actual target URI with an other form and clients will still be able to progress through their task.
Unfortunately, unlike the Link
header as defined by RFC 5988, which can define multiple relation names on the same URI (see the examples; Link: <.../checkout-wizard-p1>; rel="create-form https://acme.com/rel/checkout"
), HAL JSON only allows to define one link relation name per URI AFAIK.
To an arbitrary client a link-relation name is just an arbitrary string. Also, the Web Linking extension mechanism does not have to point to a human-readable documentation to start with, though support for such relation names may be added later on through updates or plug-ins. The point here is, a client that understands a certain link relation name can act accordingly and will use the URI just to send requests to it. Link relations might be used in terms of a rule-engine that triggered accompanying URIs when certain criteria are met, i.e. the availability of a certain client state and the presence of link relations pointing to the same URI, as in this example with the given link-relation names.
Upon requesting the content of the URI used for either the create-form
or the https://acme.com/rel/checkout
relationship the server might return a HAL-FORMS representation that allows a client to enter the desired customer information:
{
"_links": {
"self": {
"href": "https://shop.acme.com/checkout-wizard-p1"
},
"_templates": {
"default": {
"contentType": "application/x-www-form-urlencoded",
"key": "default",
"method": "POST",
"properties": [
{
"name": "firstName",
"prompt": "First Name",
"readOnly": false,
"regex": "^[A-Z][a-z]{1,24}$",
"required": true,
"templated": false,
"value": "",
"maxLength": 25,
"minLength": 2,
"placeholder": "Your first name",
"type": "text"
},
...
],
"target": "https://shop.acme.com/tmp/ea2b3fb1-c640-40e2-b16a-f7433dee6ba2",
"title": "Checkout Wizard - Customer Information"
}
}
}
}
Similar to traditional HTML forms, HAL-FORMS teaches clients about the target URI to send the request to, the HTTP operation to use as well as the media-type to marshal the request to before sending the request. In contrast to HTML however HAL-FORMS defaults here to application/json
rather than to application/x-www-form-urlencoded
. Surprisingly only those two media-types are supported by HAL-FORMS though.
The overall structure a resource supports is also taught via the elements contained within the properties
property. Like in HTML forms, the type of the presented input can be defined, which can be one of text
, hidden
, textarea
, search
, tel
, url
, email
, password
, date
, month
, week
, time
, datetime-local
, number
, range
or color
. Via an optional options
element UI controls such as (multi-select) options, checkboxes and radio-buttons can be represented as well.
Once a client entered its data and send it to the server, the server can simply respond with the next HAL-FORMS representation asking for further input such as the shipping address and so on. Here, the interaction designer might lean towards updating the temporary resource (i.e. https://shop.acme.com/tmp/ea2b3fb1-c640-40e2-b16a-f7433dee6ba2
in this scenario) directly by using PUT
, which HAL-FORMS support in contrast to HTML forms, though this would require to pass along all of the previous data as well, which makes the wizard more or less ... redundant. The next approach would be to make use of PATCH
as HTTP operation to perform a partial update on the temporary resource, though as HAL-FORMS currently only supports application/json
and application/x-www-form-urlencoded
there is no way to inform a server about (implicit) instructions to transform the temporary resource to a desired outcome, as you would do with application/json-patch+json or application/merge-patch+json.
I therefore prefer to stick to POST
here and create new temporary resources for each wizard page. Through support of hidden properties passing along all of the previous transmitted properties would easily be possible, though instead of passing along all the data it might be enough just to provide the URI of the temporary resources as hidden property. The final step could then "merge" the information in those temporary resources into one cohesive state, perform a clean up on the temporary resources affected and present the information for a last confirmation to the user in which case the actual resource is created.
While the actual form resources, that are obtained via GET
, are cacheable, the temporary resources aren't as they are only operated on via unsafe operations, which would invalidate stored representations at (intermediary) caches anyway. Caching of form resources isn't a bad idea on top of it as new properties for resources aren't introduced all of the time and thus caching allows to reduce the overhead of transmitting that representation from the server to the client. In case an update to the form is done, an update on that resource, i.e. by uploading a new version via PUT
or PATCH
, would automatically invalidate any stored representations for that resource and server clients with the new version, bypassing the cache the first time until the response was added to the cache.
While Nicholas mentioned that REST should only be used in case all of his 4 points can be answered with yes, where some points such as the encryption of documents targets more the transport channel (i.e TLS over HTTP or HTTPS or the media-type negotiated) rather than the interaction concepts proposed by the REST architecture, in my sense REST should be aimed for in case your service should last for years to come, support a plethora of different clients and enable support for future evolution, like introducing new fields on a resource down the road, without having to fear breaking clients.
Long story short, designing a wizard-like interaction through HATEOAS does make sense, especially when form-like representations are used to teach clients on where to send the data to, what HTTP method to use and which representation format to send the data in. A form also helps teaching a client on the supported properties a resource has. The tricky part for use is how the data provided through the different pages of the wizard are combined and presented to the client. While PUT
and PATCH
might be attractive at first, at a more narrow glance they might not be ideal due to restrictions on the media-type or the HTTP operation itself.