Keep in mind I have a rudimentary understanding of REST. Let's say I have this URL:
http://api.animals.com/v1/dogs/1/
And now, I want to make the server make the dog bark. Only the server knows how to do this. Let's say I want to have it run on a CRON job that makes the dog bark every 10 minutes for the rest of eternity. What does that call look like? I kind of want to do this:
URL request:
ACTION http://api.animals.com/v1/dogs/1/
In the request body:
{"action":"bark"}
Before you get mad at me for making up my own HTTP method, help me out and give me a better idea on how I should invoke a server-side method in a RESTful way. :)
EDIT FOR CLARIFICATION
Some more clarification around what the "bark" method does. Here are some options that may result in differently structured API calls:
- bark just sends an email to dog.email and records nothing.
- bark sends an email to dog.email and the increments dog.barkCount by 1.
- bark creates a new "bark" record with bark.timestamp recording when the bark occured. It also increments dog.barkCount by 1.
- bark runs a system command to pull the latest version of the dog code down from Github. It then sends a text message to dog.owner telling them that the new dog code is in production.
Most people use POST for this purpose. It is appropriate for performing "any unsafe or nonidempotent operation when no other HTTP method seems appropriate".
APIs such as XMLRPC use POST to trigger actions that can run arbitrary code. The "action" is included in the POST data:
The RPC is example is given to show that POST is the conventional choice of HTTP verbs for server-side methods. Here's Roy Fielding thoughts on POST -- he pretty much says it is RESTful to use the HTTP methods as specified.
Note that RPC itself isn't very RESTful because it isn't resource oriented. But if you need statelessness, caching, or layering, it's not hard to make the appropriate transformations. See http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ for an example.
Earlier revisions of some answers suggested you use RPC. You do not need to look to RPC as it is perfectly possible to do what you want whilst adhering to the REST constraints.
Firstly, don't put action parameters in the URL. The URL defines what you are applying the action to, and query parameters are part of the URL. It should be thought of entirely as a noun.
http://api.animals.com/v1/dogs/1/?action=bark
is a different resource — a different noun — tohttp://api.animals.com/v1/dogs/1/
. [n.b. Asker has removed the?action=bark
URI from the question.] For example, comparehttp://api.animals.com/v1/dogs/?id=1
tohttp://api.animals.com/v1/dogs/?id=2
. Different resources, distinguished only by query string. So the action of your request, unless it corresponds directly to a bodyless existing method type (TRACE, OPTIONS, HEAD, GET, DELETE, etc) must be defined in the request body.Next, decide if the action is "idempotent", meaning that it can be repeated without adverse effect (see next paragraph for more explanaton). For example, setting a value to true can be repeated if the client is unsure that the desired effect happened. They send the request again and the value remains true. Adding 1 to a number is not idempotent. If the client sends the Add1 command, isn't sure it worked, and sends it again, did the server add one or two? Once you have determined that, you're in a better position to choose between
PUT
andPOST
for your method.Idempotent means a request can be repeated without changing the outcome. These effects do not include logging and other such server admin activity. Using your first and second examples, sending two emails to the same person does result in a different state than sending one email (the recipient has two in their inbox, which they might consider to be spam), so I would definitely use POST for that. If the barkCount in example 2 is intended to be seen by a user of your API or affects something that is client-visible, then it is also something that would make the request non-idempotent. If it is only to be viewed by you then it counts as server logging and should be ignored when determing idempotentcy.
Lastly, determine if the action you want to perform can be expected to succeed immediately or not. BarkDog is a quickly completing action. RunMarathon is not. If your action is slow, consider returning a
202 Accepted
, with a URL in the response body for a user to poll to see if the action is complete. Alternatively, have users POST to a list URL like/marathons-in-progress/
and then when the action is done, redirect them from the in progress ID URL to the/marathons-complete/
URL.For the specific cases #1 and #2, I would have the server host a queue, and the client post batches of addresses to it. The action would not be SendEmails, but something like AddToDispatchQueue. The server can then poll the queue to see if there are any email addresses waiting, and send emails if it finds any. It then updates the queue to indicate that the pending action has now been performed. You would have another URI showing the client the current state of the queue. To avoid double-sending of emails, the server could also keep a log of who it has sent this email to, and check each address against that to ensure it never sends two to the same address, even if you POST the same list twice to the queue.
When choosing a URI for anything, try to think of it as a result, not an action. For example
google.com/search?q=dogs
shows the results of a search for the word "dogs". It does not necessarilly perform the search.Cases #3 and #4 from your list are also not idempotent actions. You suggest that the different suggested effects might affect the API design. In all four cases I would use the same API, as all four change the “world state.”