Manipulating Server-side Objects via REST

I’m working on a virtual tabletop for traditional adventure games, including roleplaying games and miniatures wargames. I’m currently referring to it as “Project Adventurology,” and it started as much as a learning tool as a project that may eventually see public release. I have certainly learned a lot in the months that I’ve been working on Adventurology, although sometimes it’s been more of a struggle than a frolicking adventure. The area that I’m currently struggling with is designing the REST API to enable manipulation of server-side objects.

In the spirit of KISS, there’s currently only one server-side class in the application.1 It’s called Spinner, and it is very simple indeed: it stores an integer value and a label. Spinner has methods for increasing or decreasing its value by one, and that’s it.

It’s dead simple, but I’ve struggled with the “best” way to expose those two methods, increment and decrement, via REST. Initially, I exposed them in a way that is very similar to the class’s API. You can retrieve a particular spinner with a GET to /api/spinners/{id}. That returns a nice JSON object.2 You can increase the spinner’s value with an empty POST to /api/spinners/{id}/increment, which just returns the new value (in JSON). The endpoint for decreasing the value is just /api/spinners/{id}/decrement with the corresponding response. These endpoints should really return the whole object, just for the sake of completeness, but the bigger issue that now I have verbs in my endpoints. That’s a major REST no-no!

My motivation for doing it this way is that I don’t trust the client, so I don’t want to allow it to PUT or PATCH spinners. If a client can only manipulate the object on the server, rather than completely updating its values, then the server-side object is authoritative, and trust is handle by the interface. The problem with this model is that while the endpoints make sense, it’s not obvious which HTTP verb to use with them. If there was a DO verb, that would be perfect. I went with POST because it’s generally accepted as a non-idempotent catch-all, but POST doesn’t make a lot of sense, in terms of the semantics of the API, especially since the client is POSTing an empty payload.

Fine, so I should let clients PATCH the spinner endpoint /api/spinners{id}, but I still don’t trust them. The server will need to sanitize and validate the incoming data. I worked out a very detailed way to handle this. The client would have to pass in the previous state of the object, an identifier for an operation it performed, and the new state of the object. There might have been some hashes in there, too. I don’t remember, but the goal is to allow the server to verify that the operation performed by the client is legal.

Then I realized that the server doesn’t need all that information from the client to validate the operation. The server can just compare it’s copy of the object to the copy received from the client. If the difference is allowed, then the server should store the new state and send it back to the client as verification. That seems pretty simple and straightforward.

And it is trivial. For the Spinner class. Which is pretty trivial itself.

I’m concerned about what’s going to happen with a larger, more complex object. How do you find the differences? How do you make sure the changes are allowed? On the face of it, that looks like it will involve a lot of different checks and tests, a lot of code customized for each class, and a lot of places for errors to creep in.

At this point in the analysis, the original verb endpoints start to look good again, because they make the server-side pretty straightforward. It also makes the complex PATCH payload look—not good, but less bad, because it tells the server where the changes were made.

After noodling on this for a while, I realized that we can combine all of these ideas, keeping the advantages of each while throwing out the bad parts, and end up with a nice API that follows REST standards.

To change the value of a spinner, the client needs to PATCH /api/spinners/{id}/value by sending the new value (in JSON format).3 The endpoint is all nouns, and the HTTP verb says what the client wants to do. That meets REST standards. On the back-end, the server knows what part of the object is getting updated, so it is easy to validate the changes and give the client a meaningful response. This design pattern will scale easily, both in the API and on the server-side, to larger and more complex objects. It also has advantages for hypermedia-driven navigation, but that’s beyond the scope of this article.

I think this pattern will result in a really clean, predictable API design. I’m excited to put it into use and try it out. I’ll report back here after I’ve introduced the rubber to the road, but in the meantime, what do you think? Are you going to steal this pattern for your own API, or is there a better solution that I’m not seeing?

  1. Ok, there are a lot of server-side classes, but they are internal to the “black box” of the back-end. What I mean, technically speaking, is that there is only one class that is exposed by the API. [return]
  2. I worry that “JSON object” is redundant, in the same way that “Please RSVP” is. After really noodling on the issue, I don’t think it is. JSON is a format, after all, and “XML object” isn’t redundant. Even if the ‘O’ in JSON stands for “object”. [return]
  3. I contemplated forcing the client to send the whole object here, because it would provide some additional details to perform a sanity check on the data from the client. Then I realized that that would technically be a PUT operation. Instead, I will require the object’s ID and it’s new value. The payload ID should match the URL ID field, which will give us a basic sanity check. We will also need to verify that the change in value is allowable. [return]