As a part of capabilities discovery for a resource provided by a RESTful API, I am looking for a way for the service to announce accepted values for an attribute. Consider the following example, where an apple
resource has an attribute color
:
GET /apples/17
This request yields:
{
"name": "My yummy apple",
"color": "green"
}
For a client to understand what color
values are valid when for instance PUT
ting a new version of this apple, I can think of many possible ways. However I haven't found any best practices here. The HTTP OPTIONS verb seems not to be made for this fine-grained kind of discovery. Should I just add an array attribute to the /apples
collection:
GET /apples
Response:
{
...
"colorValues": ["red", "green"]
}
Are there any better and more commonly used ways?
EDIT:
Just realized that one possible way would be to add a resources for schemas for all "real" resources. Something liked GET /schemas/apple
that would yield a JSON Schema representation for the apple
resource. Modified example from json-schema.org:
{
"id": "http://foo.bar/schema#",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "schema for an apple resource",
"type": "object",
...
"colorValues": {
"enum": [ "red", "green" ]
}
}
I have not found any examples of this though.
JSON Hyper-Schema is great for this kind of thing.
GET /apples/17
HTTP/1.1 OK
Content-Type: application/json; profile="/schema/apple"
{
"id": 17,
"name": "My yummy apple",
"color": "green"
}
A client can then dereference the hyper-schema at schema/apple
to learn what links are available to follow next.
GET /schema/apple
HTTP/1.1 OK
Content-Type: application/schema+json
{
"id": "http://foo.bar/schema/apple",
"$schema": "http://json-schema.org/draft-04/hyper-schema#",
"type": "object",
"properties": {
"id": { "type": "string", "readOnly": true },
"name": { "type": "string" },
"color": { "enum": ["red", "green"] }
},
"required": ["id", "name", "color"],
"links": [
{ "rel": "self", "href": "/apple/{id}" },
{
"rel": "http://foo.bar/relation/edit",
"href": "/apple/{id}",
"method": "PUT",
"schema": { "$ref": "#" }
}
]
}
This is a JSON Schema like you are familiar with, but contains an extra keyword links
that describes links you can follow from this resource. The client evaluates the href
URI Templates using the values from the original JSON data. So, in this case, the link evaluates to ...
{
"rel": "http://foo.bar/relation/edit",
"href": "/apple/17",
"method": "PUT",
"schema": { "$ref": "#" }
}
This link directs the client that it can make a PUT
request to /apple/17
and the request body should validate against the schema at /schema/apple
({ "$ref": "#" }
means this schema).
This gives you both a human readable and a machine executable description of your functionality. The machine executable part is a big deal because changes can often be made to your API without breaking existing clients.
NOTE: This code is written in JSON Hyper-Schema draft-04. There is a new version draft-05 that came out recently. It makes a few controversial changes. I'm still recommending draft-04 at this point. The specification can be found at https://datatracker.ietf.org/doc/html/draft-luff-json-hyper-schema-00.