- Version: 0.0.1
- Status: Draft
This specification defines an extension to the HAL json format to improve the evolvability of HAL based APIs. This specification is based of the work done by Dwolla/hal-forms and # vnd.error.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.
JSON format is described using MSON.
Table of Contents
- Dwolla HAL Form Profile
Evolvability is important for any API with more than one client. The HAL specification requires API consumers have deep knowledge of the domain and implementation details of the API provider in situations that require user input or resulting unsafe requests. This deep knowledge inherently couples API consumers to the provider(s) reducing the evolvability of the overall system.
Forms are well tested way to reduce the coupling between API consumers and providers. This form system takes a highly pragmatic approach similar to the approach of HAL to APIs.
This extension is to be used as a profile link (RFC6906) in conjunction with a HAL style media type.
Examples
application/hal+json; profile="https://github.com/jbadeau/hal-schema-forms"
This spec extends the HAL format by adding the reserved property _forms
. This property MAY appear on any HAL document (including embedded HAL documents).
Example HAL document with a form
{
"_links":{
"self":{
"href":"http://api.example.com/customers"
}
},
"_forms":{
"default":{
"_links":{
"target":{
"href":"http://api.example.com/customers"
}
},
"method":"POST",
"contentType":"application/json",
"schema":{
"title":"A registration form",
"description":"A simple form example.",
"type":"object",
"required":[
"name",
"email",
"password"
],
"properties":{
"username":{
"type":"string",
"title":"Username",
},
"email":{
"type":"string",
"title":"Email"
},
"password":{
"type":"string",
"title":"Password",
"minLength":10
}
}
}
}
}
}
-
_forms
(object, optional)-
default
(form, optional)The default form for the context. API providers SHOULD use this to indicate the best form for consumers with limited domain knowledge.
-
form id(custom string) (form, optional)
A form related to the context. A verb first clauses expressing a command (such as,
create-customer
,search-customers
) is RECOMMENDED for form id.
-
A form object is a recipe for making complex API requests.
-
_links
-
target
(link object, required)The resource which accepts submission of this form. The target of a form MAY be a [templated link](https://tools.ietf.org/html/draft-kelly-json-hal-08#section-5.2
-
-
method
(enum, required)The HTTP method to use when submitting this form. Consumers MUST ignore the case of values of this property.
Consumers SHOULD support the following values.
GET
DELETE
PATCH
POST
PUT
Producers MAY use values outside this set. Consumer SHOULD ignore forms whose
method
they don't understand. Forms with amethod
outside the list above will likely be ignored by most consumers. -
contentType
(string, optional)The media type of submissions required by the target resource. This should be the value of the
Content-Type
header. ThecontentType
property is REQUIRED whenmethod
isPATCH
,POST
orPUT
.Consumers SHOULD accept
application/x-www-form-urlencoded
,multipart/form-data
,application/json
and any media type ending in+json
. Producers MAY use media types outside this set. Consumers SHOULD ignore forms that use media types they don't understand. Forms with a content type outside the set above will likely be ignored by most consumersClients MUST follow JSON encoding rules for media types ending in
+json
. -
schema
(object[field], required)
A schema must be a valid JSON Schema object.
To submit a form API consumers will
- collect information for each field from the user
- resolve the target URL
- construct submission body
- make submission request
The submission requests MUST
- be to the resolved target URL
- use the
method
specified by the form - have a body if the method allows it
- have a
Content-Type
header field whose value is thecontentType
of the form if there is a body
The target of a form MAY be a templated link. If the target link is not templated it's href
MUST be used verbatim.
When target link is a template it MUST be expanded using the form fields in order to resolve it into a actual URL. Form fields must be converted in to a set of variable definitions whose values are the field values encoded using the form value transcoding rules. The templated is then expanded using that set of variable definitions using the process define by RFC 6570.
For example, this form
{
"_links":{
"target":{
"href":"http://example.com/customers{?cust_id,name}",
"templated":true
}
},
"contentType":"application/json",
"method":"GET",
"schema":{
"properties":{
"cust_id":{
"type":"string",
"title":"Customer ID"
},
"name":{
"type":"string",
"title":"Name"
}
}
}
}
might resolve to any of the following depending on the user input
http://example.com/customers?cust_id=42
http://example.com/customers?name=frolic
http://example.com/customers?cust_id=42&name=frolic
A field MAY be used in both URL expansion and body construction.
API producers MUST NOT include fields on forms whose method is GET or DELETE and whose target link is non-templated. Client SHOULD ignore fields on forms whose method is GET or DELETE and whose target link is non-templated.
Error responses must be valid # vnd.error documents
{
"message":"Registration failed",
"logref":42,
"traceref": "2q4736b867b8331be5de7463179c3de1",
"_links":{
"describes":{
"href":"http://path.to/describes"
},
"help":{
"href":"http://path.to/help"
},
"about":{
"href":"http://path.to/registration"
}
},
"_embedded":{
"errors":[
{
"message":"Password must contain at least three characters",
"path":"/password",
"_links":{
"about":{
"href":"http://path.to/registration"
}
}
}
]
}
}