Custom actions in your REST API

An updated article on this topic (with a different recommendation) is provided here, as part of my series of articles on RESTful API design.

While working on a REST API for Humanitarian ID, I came across a tricky (and therefore interesting) use case for a REST API: custom actions.

What are custom actions ?

Examples of custom actions are voting on a resource, subscribing to a service, starring or following a resource etc... basically any action which doesn't fit in the CRUD world. While CRUD operations are fairly easy to handle restfully, custom actions do not fit so well in the REST paradigm, so how can we handle them "RESTfully".

REST Principles

REST principles actually state that resources should be named with nouns, while an HTTP Verb (such as GET, POST, PUT or DELETE) should be used to describe an action on these resources. For example:

  • GET /users: read all users

  • GET /users/12: read user with ID 12

  • POST /users: create a new user

  • PUT /users/12: update user with ID 12

  • DELETE /users/12: delete user with ID 12

Let's say I want to add an action which subscribes a user to a list (another type of resource in my API). What are the options ?

Action in the URL

The first idea I had was to add an action verb in the URL, to have something like:

  • PUT /users/12/subscribe

This is, however, NOT Restful. Why ? Because there is an action, a verb, in the URL, and REST principles state that the action should only be in the HTTP verb, while URLs should be used to identify resources only.

While you can create a perfectly working API with an action in the URL, it will not, in the strict sense of the term, be restful.

Thinking RESTFUL: subscriptions

Instead of thinking in terms of actions, such as subscribing/unsubscribing a user to/from a list, RESTful tells us to think in terms of resources. When subscribing a user to a list, we are actually creating a subscription resource. The following 3 alternatives ARE RESTFUL, and the one you chose will actually depend on your underlying data model and where you want your subscriptions to be stored.

Option 1: subscriptions as a new resource type

This would be something like:

  • GET /subscriptions
  • POST /subscriptions
  • PUT /subscriptions/1
  • DELETE /subscriptions/1

Each subscription resource would hold a reference to a user and to a list.

Option 2: subscriptions belonging to a user

This would be something along the lines of:

  • GET /users/12/subscriptions
  • POST /users/12/subscriptions
  • PUT /users/12/subscriptions/1
  • DELETE /users/12/subscriptions/1

Subscriptions would be stored in the user object, and would contain a reference to a list.

Option 3: subscriptions belonging to a list

Here, we would have something like:

  • GET /lists/16/subscriptions
  • POST /lists/16/subscriptions
  • PUT /lists/16/subscriptions/1
  • DELETE /lists/16/subscriptions/1

Subscriptions would be stored in the list object, and would contain a reference to a user.

The choice between options 1, 2 and 3 will probably depend on where it is more convenient for your app to have the subscriptions stored. If you often need to fetch the list of subscriptions of a user, you are probably better off going for option 2: it depends on your requirements. That said, all of the options above are RESTful.

This series of articles about Restful API design is also available as an eBook on Amazon, if you need it in a handy format.

Comments

What if you wanted to create an action url for lets say.. power off virtual machine? /VirtualMachine/12345/PowerOff 

And if you say to create a "virtual machine state" property on the virtual machine class or create new object, then does it then require MORE WORK to process the entire passed in objects and spot the changes, rather than getting a direct action, with a small specific order/command?

I suppose here you'll have a clear trade-off: time spent on implementation vs non-RESTful action, which may lead to lower comprehensiveness of the API. 

Add new comment

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
10 + 2 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
By submitting this form, you accept the Mollom privacy policy.