5

For example we have entity object:

{
    "id": 10,
    "name": "First object",
    "status": "new",
    "manager_id": 200,
    "links": [
        {
            "href": "self",
            "type" : "PUT",
        }
    ]
}

How can the API express through HATEOAS that the field status can only have the values new and completed (but not e.g. canceled)?

2
  • 1
    Not sure why this question got a down-vote, and also not sure why someone voted to close it as off-topic. This is not about assistance with writing or debugging code. It's a conceptual question about HATEOAS. I think it's a good fit for this site. – Greg Burghardt Sep 28 '18 at 14:18
  • One word curies. The documentation doesn't need to be human readable. It could be a JSON document. As for instance the famous swagger data model, based on OpenAPI – Laiv Nov 15 '18 at 8:50
0

Typically, you do it in one of two ways

In the world of HTML, unsafe operations were described by forms, and the description of the form could include hints for the client as to what possible values were expected.

In a world where you are manipulating representations of the resources directly, this is normally achieved by documenting a schema for the representation, and within that schema documenting the restrictions on the information contained within.

For example, the JSON Patch specification documents what operations are supported. Sticking in anything else means that you aren't providing a valid application/json-patch+json document.

But if I'm reading your example correctly, what you are trying to do is allow the client to drive some resource to the next state of an FSM. As far as I can tell, nobody is claiming that's a good fit for the PUT verb

Clients should never decide the current state of an entity, instead, they should request transitions of that entity to the desired state. -- Richard Clayton

PUT (and similarly PATCH) are good fits for remote authoring: situations where the client is the authority for the information, and is just sending to the server a cacheable copy. It's not as a good a fit for manipulating resources where the server, rather than the client, is the source of truth.

1
  • I strongly disagree with the notion that PUT and PATCH aren't suitable for manipulating server-owned resources. For starters, they're the only idempotent mutation methods besides DELETE; if you do every mutation with POST you lose the ability for proxies to transparently retry failed requests. It also makes caching proxies less useful. – Jack Nov 16 '18 at 7:48
4

JSON Hyper-Schema is great for this kind of thing. It allows you to define a JSON Schema for the JSON you expect from the request.

GET /my-resource/10

HTTP/1.1 200 OK
Content-Type: application/json
Link: </schema/my-resource>; rel="describedby"

{
  "id": 10,
  "name": "First object",
  "status": "new",
  "manager_id": 200
}

GET /schema/my-resource

HTTP/1.1 200 OK
Content-Type: application/schema+json

{
  "$schema": "http://json-schema.org/draft-04/hyper-schema#",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string" },
    "status": { "enum": ["new", "completed", "canceled"] },
    "manager_id": { "type": "integer" }
  },
  "links": [
    { "rel": "self", "href": "/my-resource/{id}" },
    {
      "rel": "edit",
      "href": "/my-resource/{id}",
      "schema": {
        "allOf": [{ "$ref": "#" }],
        "properties": {
          "status": { "enum": ["new", "completed"] }
        }
      }
    }
  ]
}

This tells a user agent that you can edit (defaults to PUT) this resource and the resource must conform to the schema given which is the resource's schema with an extra constraint.

1

HATEOAS is just a lame acronym for "putting links and forms in your response".

The response format (not REST or HATEOAS) determines how those are expressed, and the degree to which a resource can specify the constraints of any user-generated values for templated links.

Your best option is to find or invent a response format that contains the necessary levers to be able to specify your constraints to the degree you require.

It is very unlikely that a format exists that will be able to specify everything perfectly, for example, in HTML, a search form with two inputs cannot specify that the combined length of the resultant GET url be less than whatever maximum length your server can handle (say, 4096 bytes). And curl is able to generate any input anyway, so your resource has to be able to identify and reject invalid inputs.

1

I would not reinvent the wheel and I would take a look at some of the HATEOAS implementations out there, as for instance, HAL by Mike Kelly.

HAL introduces something named CURIEs through which, we can label links and content. These labels are tied to an API Model documentation, which it's also discoverable by clients since they are represented as links in the object model.

"_links": {
  "curies": [
    {
      "name": "doc",
      "href": "http://haltalk.herokuapp.com/docs/{rel}",
      "templated": true
    }
  ],

  "doc:latest-posts": {
    "href": "/posts/latest"
  }
}

Where these links lead to is another subject. They could lead us to static JSON files where we type down our own schemes, or as I have implemented once, they could be linking Swagger's endpoints. Swagger's document model is based on OpenAPI which has been battle tested enough for the community for me to adopt it.

Both, HAL and Swagger (OpenAPI) have several tools and libraries (for different technologies) for you to forget reinventing the wheel (again) on the client-side.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.