- Version: 0.0.2
- Status: Draft
This specification defines an extension to the HAL json format to improve the evolvability of HAL based APIs.
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/dwolla/hal-forms"
application/vnd.dwolla.v1.hal+json; profile="https://github.com/dwolla/hal-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/hal+json",
"fields": [
{
"name": "name",
"path": "/name",
"type": "string",
"value": "Dwolla",
"displayText": "Name",
"validations": {
"required": true
}
},
{
"name": "email",
"path": "/email",
"type": "email",
"displayText": "Email",
"validations": {
"required": true
}
},
{
"name": "password",
"path": "/password",
"type": "sensitive",
"displayText": "Password",
"validations": {
"required": true
}
},
{
"name": "businessType",
"path": "/businessType",
"type": "string",
"displayText": "Business Type",
"validations": {
"required": true
},
"accepted": {
"values": [
{
"value": "corporation",
"key": "CORPORATION",
"displayText": "Corporation"
},
{
"value": "llc",
"key": "LLC",
"displayText": "LLC"
},
{
"value": "partnership",
"key": "PARTNERSHIP",
"displayText": "Partnership"
},
{
"value": "soleproprietorship",
"key": "SOLEPROPRIETORSHIP",
"displayText": "Sole Proprietorship"
}
]
}
},
{
"name": "businessClassification",
"path": "/businessClassification",
"type": "string",
"displayText": "Business Classification",
"validations": {
"required": true
},
"accepted": {
"groupedValues": [
{
"key": "FOOD_RETAIL_AND_SERVICE",
"displayText": "Food retail and service",
"values": [
{
"value": "breweries",
"key": "BREWERIES",
"displayText": "Breweries"
},
{
"value": "distilleries",
"key": "DISTILLERIES",
"displayText": "Distilleries"
}
]
},
{
"key": "MANUFACTURING",
"displayText": "Manufacturing",
"values": [
{
"value": "computers",
"key": "COMPUTER_AND_ELECTRONIC_PRODUCT_MANUFACTURING",
"displayText": "Computer and electronic product manufacturing"
},
{
"value": "furniture",
"key": "FURNITURE_AND_RELATED_PRODUCT_MANUFACTURING",
"displayText": "Furniture and related product manufacturing"
}
]
}
]
}
}
]
}
}
}
-
_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
. -
fields
(array[field], required)The fields in this form.
A field object describes a value that can be submitted as part of this form.
Example field object
{
"name": "name",
"path": "/name",
"type": "string",
"value": "Dwolla",
"displayText": "Name",
"validations": {
"required": true
}
}
-
name
(string, required)Identifies the field. Consumers MAY use this to locate fields of interest.
-
path
(string, optional)A JSON Pointer (RFC6901) to a field in a resource typically being created as a result of a submission to the API. The
path
property is REQUIRED whencontentType
is JSON. Thepath
property SHOULD be omitted when the form'smethod
isGET
orDELETE
or thecontentType
isapplication/x-www-form-urlencoded
ormultipart/form-data
. -
value (*, optional)
The current/persisted value of the field.
-
type
(enum, required)Provides a hint indicating the type of values of this field and what UI element(s) would be appropriate to present to the user. This list MAY expand over time. Clients SHOULD treat unrecognized field types as
string
.Possible types at this time:
-
boolean
True or false value.
-
number
Arbitrary precision decimal numbers.
-
string
Short, probably single line, series of characters. Consumers should present the user with a single line text entry box.
-
date
Calendar date representing a specific year, month and day. Dates are independent of time zones.
-
time
Time of day. Times MAY specify a time zone.
-
datetime
Calendar date and time. Datetimes MAY specify a time zone.
-
sensitive
string
whose value should be obscured in the user interfaces and logs. -
hidden
Field needed by the form's target but that is not user editable.
-
text
Potentially long, multi-line, string. Analogous to textarea in HTML. Consumers should present the user with a multi-line text entry area.
-
email
Email address.
-
tel
Telephone numbers. API consumers SHOULD support international phone numbers.
-
file
File picker. Value will be the contents of the selected file.
Forms using this field type MUST use
multipart/form-data
as theircontentType
.
-
-
displayText
(string, optional)A human readable string that describes the field. This SHOULD be used in place of a client's own display text. Clients SHOULD use
name
if this is missing. -
validations
(validation, optional)An object with rules specifying how values of the field MAY be validated.
-
accepted
(object, optional)An object that indicates the exhaustive set of values accepted by the API for the field.
- One Of
-
values
(array[value], required)Simple list of acceptable values.
-
groupedValues
(array[value group], required)List of groups of values that are acceptable.
-
- One Of
-
multiple
(boolean, optional)A boolean value which indicates whether multiple values MAY be submitted for the field. Defaults to
false
if not provided, meaning multiple values MUST NOT be submitted. If provided andtrue
, values MUST be submitted as a collection/array of values.
A description of syntactic restrictions on the containing field. This information MAY be used by the client to provide feedback to users without requiring a round trip to the server. API providers MUST validate submitted forms for validity regardless of the validation information embedded in the form.
Example validation object
"validations": {
"required": true,
"regex": "^\d{3}-?\d{2}-?\d{4}$"
}
-
required
(boolean, optional)True indicates that forms submitted without a value for this field will be rejected by the API provider. API consumers SHOULD treat this as false if it is absent.
-
regex
(string, optional)A Perl compatible regular expression describing allowed syntax of this field's values. API providers SHOULD omit this property unless the field type is
string
ortext
. API consumers MUST ignore this property unless the field type isstring
ortext
A group of valid values for the containing field.
Example of value group
{
"key": "MANUFACTURING",
"displayText": "Manufacturing",
"values": [
{
"value": "computers",
"key": "COMPUTER_AND_ELECTRONIC_PRODUCT_MANUFACTURING",
"displayText": "Computer and electronic product manufacturing"
},
{
"value": "furniture",
"key": "FURNITURE_AND_RELATED_PRODUCT_MANUFACTURING",
"displayText": "Furniture and related product manufacturing"
}
]
}
-
key
(string, required)An identifier for the group of values. Consumers MAY use this for identification and display.
-
displayText
(string, optional)A human readable description of this value. If present, consumers SHOULD use this when displaying the value to users.
-
values
(array[value], required)List of values, one of which must be used in the form submission if this value group is selected.
A single valid value for the containing field.
{
"value": "breweries",
"key": "BREWERIES",
"displayText": "Breweries"
},
-
key
(string, required)An identifier for the value. Consumers MAY use this for identification and display.
-
displayText
(string, optional)A human readable description of this value. If present, consumers SHOULD use this when displaying the value to users.
-
value
(*, required)The value that MUST be used in the form submission if this value is selected.
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/x-www-form-urlencoded",
"method": "GET",
"fields": [
{ "name": "cust_id",
"type": "string" },
{ "name": "name",
"type": "string" }
]
}
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.
If the form's method allows a body (PUT
, POST
or PATCH
) then the form is used to construct a body document for the form submission. The exact algorithm for body construction varies based on the content type of the form. Forms with application/x-www-form-urlencoded
or multipart/form-data
content types use form transcoding. Forms with application/json
or +json
content types use JSON transcoding.
Form encoding applies to forms whose contentType
is application/x-www-form-urlencoded
or multipart/form-data
. For form encoding each field of the form is as a name-value pair. Values are converted into a string base on type specific rules specified below. The resulting name-value pairs are then encoding following common encoding rules for application/x-www-form-urlencoded
or RFC7578 for multipart/form-data
.
For example, the following x-www-form-urlencoded
form
{ "_links": { "target" : { "href": "http://example.com" } },
"contentType": "application/x-www-form-urlencoded",
"method": "POST",
"fields": [
{ "name": "title",
"type": "string" },
{ "name": "recommended",
"type": "boolean" }
]
}
might encode into this body
title=User+Provided+Title&recommended=true
With similar user input the following form-data
form
{ "_links": { "target" : { "href": "http://example.com" } },
"contentType": "multipart/form-data",
"method": "POST",
"fields": [
{ "name": "title",
"type": "string" },
{ "name": "recommended",
"type": "boolean" }
]
}
encodes into this body
--AaB03x
content-disposition: form-data; name="title"
User Provided Title
--AaB03x
content-disposition: form-data; name="recommended"
true
--AaB03x
-
boolean
literal
true
andfalse
UTF-8 strings -
number
Decimal number encoded in UTF-8.
-
string
UTF-8 encoded characters.
-
date
Clients SHOULD encode dates as ISO 8601 encoded calendar date. Servers MUST accept ISO 8601 encoded calendar date. Servers MAY make a best effort attempt to extract a date even if the value is not a valid ISO 8601 date.
-
time
Clients SHOULD encode times as ISO 8601 encoded time. Servers MUST accept ISO 8601 encoded time. Servers MAY make a best effort attempt to extract a time even if the value is not a valid ISO 8601 time.
-
datetime
Clients SHOULD encode date times as ISO 8601 encoded date time. Servers MUST accept ISO 8601 encoded date time. Servers MAY make a best effort attempt to extract a date and time even if the value is not a valid ISO 8601 date time.
-
sensitive
UTF-8 encoded characters.
-
hidden
Use value encoding rule for the field
value
's datatype. -
text
UTF-8 encoded characters.
-
email
Clients SHOULD encode email addresses as RFC 6068 mailto URI. Servers MUST accept RFC 6068 mailto URIs. Servers SHOULD make a best effort attempt to extract an email address even if the value is not a
mailto
URI. -
tel
Clients SHOULD encode telephone numbers as RFC 3966 telephone URIs. Servers MUST accept RFC 3966 telephone URIs. Servers SHOULD make a best effort attempt to extract a phone number from value even if it is not a
tel
URI. -
file
File attached as one part of the multipart document as define by RFC 7578 Forms using this field type MUST use
multipart/form-data
as theircontentType
.
JSON transcoding applies to forms whose contentType
is application/json
or any media type ending in +json
. Field values are converted into a native JSON data type using the value trancoding rules below. Those encoded values are then added to a JSON document at the location indicated by the path
of the field. The body is complete once all fields have been inserted.
For example, the following JSON form
{ "_links": { "target" : { "href": "http://example.com" } },
"contentType": "application/x-www-form-urlencoded",
"method": "POST",
"fields": [
{ "name": "title",
"type": "string",
"path": "/title" },
{ "name": "recommended",
"type": "boolean",
"path": "/superfluous/nesting/recommended" }
]
}
would encode into this body
{ "title": "User Provided Title",
"superfluous": {
"nesting": {
"recommended": true
}
}
}
-
boolean
Built-in boolean data type. -
number
Built-in number datatype.
-
string
Built-in string datatype.
-
date
Clients SHOULD encode dates as ISO 8601 encoded calendar date. Servers MUST accept ISO 8601 encoded calendar date. Servers MAY make a best effort attempt to extract a date even if the value is not a valid ISO 8601 date.
-
time
Clients SHOULD encode times as ISO 8601 encoded time. Servers MUST accept ISO 8601 encoded time. Servers MAY make a best effort attempt to extract a time even if the value is not a valid ISO 8601 time.
-
datetime
Clients SHOULD encode date times as ISO 8601 encoded date time. Servers MUST accept ISO 8601 encoded date time. Servers MAY make a best effort attempt to extract a date and time even if the value is not a valid ISO 8601 date time.
-
sensitive
Built-in string datatype.
-
hidden
Use the field
value
verbatim. -
text
Built-in string datatype.
-
email
Clients SHOULD encode email addresses as RFC 6068 mailto URIs. Servers MUST accept RFC 6068 mailto URIs. Servers SHOULD make a best effort attempt to extract an email address even if the value is not a
mailto
URI. -
tel
Clients SHOULD encode telephone numbers as RFC 3966 telephone URIs. Servers MUST accept RFC 3966 telephone URIs. Servers SHOULD make a best effort attempt to extract a phone number from value even if it is not a
tel
URI. -
file
Unsupported. This field type MUST NOT be used with a JSON
contentType
.