Tuesday, October 28, 2014

Pressing a button


Take a blog. You create a post, but typically you don't publish it right away. (Unless you're me, impulsive and bad at proofreading.) You write a ton, edit it down to something your English teacher wouldn't spill red ink over, add some pictures and links, and then publish it. Typically you do that by pressing a single button, and often pressing that button takes you to the newly published article.

How might that translate into a RESTful client API? It starts off pretty simple - POST /articles to create, forwards to /articles/1. Edit a bit, then POST /articles/1 to update…all good so far. Now, how do I publish?

You could POST a full representation of the article, which might look like this:

{
   "text":"Ipsum dolerum",
   "published": false
}

That's annoying, because now the client has to know which property to set to publish a post.

You could use the PATCH verb, but that has the same problem as the "published" flag - the client has to know what property to set.

If you're really feeling purist today, you could use an explicit content-type with an empty body.

We could POST an application/json+articleStatus representation to /articles/1. This has some advantages, especially if the change has other properties. It's kind of heavy for a single property though.

Let's go over the principles of REST design:

  • Client–server - separation of concerns. That's exactly what we want. Clients shouldn't have to care about how the server publishes an article. Having to set a flag breaks that abstraction.
  • Stateless - everything needed to process the request is included in the request. Ok - the client should send an article id and something that shows the intention of publishing it.
  • Cacheable - no problem here, send me a no-cache header if you want.
  • Layered system - I don't care what system publishes my article.
  • Uniform interface - Now were getting somewhere!
    • Identification of resources - gotta have a url. Gotcha.
    • Manipulation of resources through these representations - Right, so the client should easily know how to publish a post.
    • Self-descriptive messages - yup, make sure the client can tell what to do.
    • Hypermedia as the engine of application state (HATEOAS) - Aha! Give back links for the client to use!




What conclusions can we draw when applying this to our API?

  1. Identify resources. We only have one - an article. That should always stand out. So, articles are the only things that have URLs. More specifically, /articles/1/publish is not a resource.
  2. If the client has a representation of an article, he should have everything he needs to publish it. That annoying "published" flag back there covers that pretty well, I guess…
  3. The client should be able to easily tell what he can do. Sorry, but that flag doesn't cut it here. Sure, he might figure it out in this case, but what if the article has more than one state, like "reviewing", "in moderation", and "published"? How would the client know how to put the resource into one of those states?
Our last option is query parameters. POST to /articles/1?publish. Now, a GET from /articles/1 will return:

{
   "metadata": {
      "publish": "/articles/1?publish",
      "save": "/articles/1"
   },
   "text": "Ipsum dolerum"
}

Now the server has fulfilled the uniform interface requirement. The article is clearly identified by the URI. The client now knows all the operations it can perform on the article. There are basic messages that describe what each operation does; these could be expanded further if necessary. And, HATEOAS is fulfilled since the server sends back the exact links the client needs to use to perform each operation.

Plus now the client doesn't have to care how an article is published. All he has to do is press a button.

No comments:

Post a Comment