Link relations for Create and Delete?

38 views
Skip to first unread message

Curtis Farnham

unread,
Jul 8, 2015, 2:29:35 PM7/8/15
to mason-me...@googlegroups.com
In building a new Mason-based API, I'm not sure what link relations to use in @controls for the various HTTP verbs that can be performed on a service.  For example, say I have these services and verbs to support:
  • /vehicles
    • GET - retrieves a collection of personal vehicles I own
    • POST - adds a new vehicle to the collection
  • /vehicles/{id}
    • GET - retrieves a single vehicle
    • DELETE - removes a vehicle from the collection
    • PATCH - updates a vehicle's record
The @controls element requires its keys to be link relations.  But according to Roy Fielding himself (father of all things Hypermedia and REST), link relations are not supposed to be conflated with HTTP verbs (see http://stackoverflow.com/a/10608030/818236).  They're supposed to be nouns, like "A is a child of "B" or "A is the author of B".

I've thought about having multiple "self" controls, one for each verb.  But since @controls is a hash, each key must be unique.

I've also thought about using the "alt" property on a control, so the primary control would be the "GET" resource and the alternates would be the other verbs. But in the Mason spec, "alt" is supposed to be only for alternate representations of the same resource (e.g. different formats for the same image.)

Any ideas??

Curtis Farnham

unread,
Jul 8, 2015, 3:01:51 PM7/8/15
to mason-me...@googlegroups.com
FWIW, I cross-posted a more generalized version of my question here: http://stackoverflow.com/questions/31301416/rest-link-relations-for-http-verbs

Jørn Wildt

unread,
Jul 9, 2015, 1:21:28 AM7/9/15
to mason-me...@googlegroups.com
I think it is understandable that link relations should not be used for describing verbs - after all the name implies how two resources *relates* to each other via the link - not how they are supposed to interact with each other.

But Mason controls are not links. Yes, they can represent links, but also more than that. You can compare Mason hypermedia controls to forms in HTML. Such forms do not have link relations but they *do* have identifiers such as the "name" and "id" attributes. If a scripted client were to look up an HTML form for a specific operation it would surely use one of these. The same apply to hypermedia controls in Mason where the object indexer is equivalent to the name/id attributes in a form.

So in your case of vehicles I would use hypermedia control names such as "my-vehicles" (which by the way is both a link relation *and* a noun), "add-vehicle" and "delete-vehicle".

This does although raise the question of relevance to the IANA link registry as it is completely unusable for anything but links in Mason.

/Jørn




--
You received this message because you are subscribed to the Google Groups "Mason media type" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mason-media-ty...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jørn Wildt

unread,
Jul 9, 2015, 2:05:04 AM7/9/15
to mason-me...@googlegroups.com
The next obstacle I have found in this area is how to represent more complex business operations beyond basic CRUD - such as "Cancel pending task" or "Close case". The first one could be done by DELETE "task" - but what if we also support actual removal/delete of X? And what if we need to include some related details when invoking the action (DELETE has no content data)?

My preferred method is 1) add one sub-resource for each complex operation, such as /tasks/{id}/cancel or /cases/{id}/close, and 2) expose these as hypermedia controls in the primary resource:

{
  Id: 1234,
  Title: "My task",
  @controls:
  {
    cancel:
    {
      title: "Cancel pending task",
      href: "http://myapi.com/tasks/1234/cancel",
      method: "POST"
    }
  }
}

This in turn invokes the discussion of "avoid verbs in URLs - always use nouns". The classic answer is "if you run out of verbs - then add new resources" which is actually what I have done in the above example, adding the "cancel" resource - a resource representing the task state and how you cancel it. Some people call such resources "controller" resources.

If you POST relevant data to the task state you may cancel it. If you GET that resource then you could end up with another Mason document describing the resource:

>>> GET http://myapi.com/tasks/1234/cancel

{
  @meta:
  {
    title: "Pending task state",
    description: "This resource describes current state of pending task and how to cancel it."
  },
  TaskId: 1234,
  State: "Pending",
  @controls:
  {
    cancel:
    {
      title: "Cancel pending task",
      href: "http://myapi.com/tasks/1234/cancel",
      method: "POST"
    }
  }
}

So how do you discover "relevant data" needed for POST to cancel the task? Use JSON schema:

{
  @meta:
  {
    title: "Pending task state",
    description: "This resource describes current state of pending task and how to cancel it."
  },
  TaskId: 1234,
  State: "Pending",
  @controls:
  {
    cancel:
    {
      title: "Cancel pending task",
      description: "By posting to this URL you cancel the task. If needed you may include an optional 'reason' for doing so.",
      href: "http://myapi.com/tasks/1234/cancel",
      method: "POST",
      schema:
      {
        properties:
        {
          reason:
          {
            description: "Include a reason for canceling the task",
            type: "string"
          }
        }
      }
    }
  }
}

Have fun :-)

/Jørn



On Wed, Jul 8, 2015 at 9:01 PM, Curtis Farnham <cfar...@phone.com> wrote:
FWIW, I cross-posted a more generalized version of my question here: http://stackoverflow.com/questions/31301416/rest-link-relations-for-http-verbs

--

Curtis Farnham

unread,
Jul 12, 2015, 12:12:32 AM7/12/15
to mason-me...@googlegroups.com
Ah, yes.  That's what I was missing in my thought process.  @controls doesn't only have links. It has forms too, and forms aren't GETting a noun, they are doing some verb to it.  So yes, I see how creating a "relation" describing the action e.g. "add-vehicle" makes sense.

Thanks Jørn!

-Curtis

Curtis Farnham

unread,
Jul 12, 2015, 12:23:56 AM7/12/15
to mason-me...@googlegroups.com
Yeah, that can be tricky when the actions don't fit the standard CRUD model.  As you suggested, I would tend to create a new action-based service like that if I ran out of other ideas that fit more closely with REST.  But in the specific example you mentioned, wouldn't it be more "RESTful" to support the PUT and/or PATCH verb on that object and include a "State" field?

{
  Id: 1234,
  Title: "My task",
  State: "Pending",
  @controls: {
    edit: {
      title: "Update this task",
      href: "http://myapi.com/tasks/1234",
      method: "PATCH",
      schema: {
        type: "object",
        properties: {
          "State": {
            "type": "string",
            "enum": ["Pending", "Canceled", "Closed"]
          },
          "Reason": {
            "type": "string",
            "description": "Include reason for updating the task"
          },
          // other properties here as needed, such as for the Title
        }
      }
    }
  }
}

-Curtis

Jørn Wildt

unread,
Jul 17, 2015, 3:47:44 PM7/17/15
to mason-me...@googlegroups.com
This is a bit difficult for me to explain, but let me try ...

Based on my prior experiences I have come to dislike PATCH for various reasons. Most of all because you cannot have side effects with it - even though I know your example is trying to do it.

In your example you PATCH "reason" as if it was a simple string property on the main resource - but I was considering "reason" as something to be written into a separate log which may, or may not, be represented as a separate resource, and certainly not be represented as a property in the main resource. And if it is not there how can I then patch it? 

Further more "reason" is added to a list of "reasons" (the log) which shows what has happend over time so if you want to patch it then it should be done as something like an "append" operation to a JSON array (which is still not part of the main resource).

Another interesting twitch you touch upon is what "schema" should describe. For Mason I expect the schema to describe the payload of the operation - in this case the PATCH operation which must take a PATCH format like for instance json-patch and the schema for that kind of document looks nothing like the schema describing what you can patch. There is a difference between the schema describing the resource and the schema describing json-patch.

PATCH can be good for simple CRUD operations but not (always) for complex bussines operations. Neither does PATCH lend itself easily to traditional forms describing various HTTP requests - at least not in any way I have seen yet.

/Jørn


--
/Jørn

Curtis Farnham

unread,
Jul 20, 2015, 7:45:04 PM7/20/15
to mason-me...@googlegroups.com
I see.  Yeah, for adding that reason parameter, that makes it more complicated and not so suited for a simple PUT or PATCH to change the status.

I actually had not heard of the JSON-Patch spec.  I totally agree, the schema for it wouldn't look anything like the object being patched.  It might not be worth implementing.  I'd have to think about that some more.

Thanks,
Curtis

Jørn Wildt

unread,
Jul 21, 2015, 1:32:21 AM7/21/15
to mason-me...@googlegroups.com
Just a few more notes: you may also want to look at "JSON merge patch" which may be a better match than JSON-Patch - see https://tools.ietf.org/html/rfc7386. With this format your original schema would be right on ... maybe JSON-merge *is* a better fit for this kind of scenarios?

The example with "reason" is not the only realistic example - you could also expect the client to send something similar to ETag values (to avoid concurrent updates) and temporary authorization tokens.

/Jørn

Curtis Farnham

unread,
Jul 23, 2015, 9:47:19 PM7/23/15
to Mason media type, j...@fjeldgruppen.dk
Sweet!  Merge patch is a lot more along the lines of what I imagined a PATCH would be in the first place.  Thanks for bringing that to my attention.

-Curtis
Reply all
Reply to author
Forward
0 new messages