1

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 colorvalues are valid when for instance PUTting 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.

2 Answers2

1

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.

Community
  • 1
  • 1
Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
0

One of possible ways is having two resources: apples and colours. It will be very easy for your potential clients to get available colours in a restful way. What about hypermedia? There are many standards, you can create your own but it's better to stick with some widely-used.

For example, if you want to implement it following HAL, getting apple will look like:

GET /apples/17

{
  "name": "My yummy apple",
  "_links": {
     "self": {
       "href": "/apples/17"
     },
   },
   "colour": {
     "id": "green",
     "href": "/colours/green"
   }
}

And what about listing all colours? Sth like this:

GET /colours

{
  "_links": {
    "self": {
        "href": "/colours"
    }
  },
  "_embedded": {
    "colours": [
      {
         "_links": {
           "self": {
             "href": "/colours/green"
           }
         },
         "id": "green"
      },
      {
         "_links": {
           "self": {
             "href": "/colours/red"
           }
         },
         "id": "red"
      }
    ]
  }
}

It's the modern way to design API adopted for example by Amazon

The Amazon AppStream web service is a resource-based API that uses Hypertext Application Language (HAL). HAL provides a standard way for expressing the resources and relationships of an API as hyperlinks. Using HAL, you use HTTP methods (GET, PUT, POST, DELETE) to submit requests and receive information about the API in the response. Applications can use the information returned to explore the functionality of the API.

Edit:

If different colours are allowed for different entities, you can do sth like

GET /apples/17

{
  "name": "My yummy apple",
  "_links": {
     "self": {
       "href": "/apples/17"
     },
     "available_colours": {
       "href": "/apples/17/available_colours"
     }
   },
   "colour": {
     "id": "green",
     "href": "/colours/green"
   },      
   "_embedded": {
     "available_colours": [
       {
         "_links": {
           "self": {
             "href": "/colours/green"
           }
         },
         "id": "green"
       },
       {
         "_links": {
           "self": {
             "href": "/colours/red"
           }
         },
         "id": "red"
       }
    ]
  }
}

As you see it's very flexible structure.

Piotr Dawidiuk
  • 2,961
  • 1
  • 24
  • 33
  • Valuable input - and I can definitely see the use! But in a more general sense (eg to convey maximum allowed value for a set of integer value attributes) it appears to me as quite tedious? Say for instance that there is a total of 5 different apple colors, but different colors are allowed for different entities? – Marcus Johansson Feb 01 '17 at 07:53
  • @MarcusJohansson Hello, I have edited the answer and added some extra example with `available_colours` for specified entity. – Piotr Dawidiuk Feb 01 '17 at 08:48
  • Thanks a lot! Appreciate your input on this. – Marcus Johansson Feb 13 '17 at 09:38