Don’t Limit Your REST API to CRUD Operations

I think one of the best things about RESTful web services is the Collection Pattern. It’s a really smart and developer-friendly way of designing a REST service.

For example, the Task REST API below, which is taken from my company’s REST API, is a typical example of how the collection pattern looks:

The collection pattern is so widely adopted that even a REST newbie, who has never seen this API before, will be able to guess the description in the Task column based on the content in the Method and Path columns.

The collection pattern is also really smart from a code perspective, because many frameworks have some sort of Active Record implementation on top of the collection pattern, so the framework can automatically wrap the whole REST service in a convenient way.

For example, in the old AngularJS framework they had this wonderful $resource factory that you could simply give the URI of a REST service that followed the collection pattern – and then the $resource factory would automatically figure out the rest (no pun intended!)

var Task = $resource('/tasks/:taskId', {taskId: '@id'});

var task = new Task();
task.description = "Put a man on the moon.";
task.assignee = "James Webb";
task.$save();

While the collection pattern is really clever and so easy to use – and the best solution for almost all REST services – there are some edge cases where its CRUD approach just doesn’t make sense and other patterns should be considered.

Now it gets controversial…

When the web was the new big thing – and online pet shops were worth hundreds of millions of dollars – you would see web pages with HTML forms like this: 

<form method="POST" action="send_mail.cgi">
    <input type="text" name="subject">
    <input type="text" name="message">
    <input type="submit" value="Send Message">
</form>

While you can long for those innocent days when you could publish code like that on the web and not be flooded with spam; the important thing here is that you can also call the send_mail.cgi script directly and use it as a web service; for example, using the small JQuery script below:

var mail = {
    subject: "Man walks on the moon",
    message: "Armstrong and Aldrin become the first men on the moon..."
};

$.post({
    url: "/send_mail.cgi",
    contentType: "application/x-www-form-urlencoded",
    data: mail,
    success: function() {
        console.log("Mail sent!");
    }
});

Now, I will argue that send_mail.cgi is a RESTful web service (!) even if it’s a really poorly designed one and a simple POST /mails service would have been a lot nicer!

If you finish the demanding, yet satisfying task of reading Roy Fielding’s PhD thesis, which defines the REST architectural style, you will see that it says nothing about limiting our REST services to CRUD operations and it also says nothing about limiting ourselves to the collection pattern…

In fact, Fielding later wrote a blog post about the use of the POST method in REST, and said, “As long as the method is being used according to its own definition, REST doesn’t have much to say about it.”  And if we read the HTTP specification we can see that it doesn’t limit the use of POST to adding new items to collections – and if we read the URI specification we can see that it doesn’t limit our URI naming to plural nouns…

So POST /send_mail.cgi is OK from a specification point of view and can be considered RESTful…

So what are you saying?

So what am I saying? Is this the sacking of Rome and we can now all go crazy with POST /add-new-order.cgi and GET /find-my-orders.xml. No laws! No limits!

Of course not. I still think that the Collection Pattern is the right choice for almost all RESTful web services – and it should be the default choice for any new RESTful web service – because it’s so widely adopted and easily recognizable by most API users.

However, there are edge cases where it makes sense to use other patterns, such as the Controller Pattern. For example, if I have a REST service for rockets then one does not simply just launch a rocket (or walk into Mordor!) because you need to provide launch codes and the rocket needs to go through multiple stages before actual take-off – and this goes way beyond just changing the value of a field in the resource representation. So for this scenario I would add a controller subresource:

/rockets/43/launch-rocket

When breaking with the Collection Pattern I really like the verb-noun naming of the URI, such as launch-rocket. This is without doubts because I read Code Complete way too many times (!) but also because it makes it obvious to the API user that it isn’t a part of the Collection Pattern. On top of that, remember to add a link to controller subresource in the resource representation to make the API user aware that the subresource exists:

{
  "id": 43,
  "name": "Apollo 11",
  "state": "Ready for launch",
  "_links": {
    "self": {"href": "/rockets/43"},
    "launch-rocket": {"href": "/rockets/43/launch-rocket"} 
  }
}

This meaning of this post isn’t to say that the Collection Pattern is bad. In fact, it’s the right choice for almost all REST APIs and everybody will love you for using it! 🙂 The purpose is to say that Collection Pattern != REST and you still have some wriggle room for edge cases that doesn’t fit neatly into the Collection Pattern without losing your API’s RESTfulness or the desirable properties that come with this architectural style.

7 thoughts on “Don’t Limit Your REST API to CRUD Operations

  1. Tommy Radley

    Hi Ken! Thank you so much for sharing your knowledge and experience! What will the response be when callin POST /rockets/43/launch-rocket?

    Kind regards,
    Tommy

    Reply
    1. Kenneth Lange Post author

      Hi Tommy,

      I would expect a HTTP status “303 See Other” and then a link back to the rocket resource where the API user can follow the launching. For example:

      HTTP/1.1 303 See Other
      Location: /rockets/43
      

      I would also expect (assuming it isn’t a reusable rocket!) that the launch-rocket link will disappear from the rocket resource after it has been launched (as you cannot launch it anymore):

      {
        "id": 43,
        "name": "Apollo 11",
        "state": "Launch initiated",
        "_links": {
          "self": {"href": "/rockets/43"}
        }
      }
      

      /Kenneth

      Reply
    1. Kenneth Lange Post author

      Hi Sanjay,

      I wouldn’t expect send_mail to be an idempotent operation; that is, if you call it twice I would expect two emails to be sent – so it would break with the HTTP definition of PUT to use it for this.

      GET /send_mail.cgi would also be a no-no as sending mails definitely has side-effects, so it would violate the HTTP specification to use GET for this operation.

      And on the other side, retrieval of email, then POST /retrieve-mail would also be wrong as GET would be a more accurate description of the operation.

      Hope this helps.

      /Kenneth

      Reply
  2. Shantel Magalong

    I was very pleased to find this web-site.I wanted to thanks for your time for this wonderful read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you blog post.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *