RESTful APIs and non-CRUD operations

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
Pesto
Posts: 737
Joined: Wed Sep 05, 2007 5:33 pm UTC
Location: Berkeley, CA

RESTful APIs and non-CRUD operations

Postby Pesto » Fri Oct 08, 2010 7:57 am UTC

A crude and inaccurate understanding of REST is that the various HTTP methods map to CRUD operations. I realize that REST != CRUD, but I'm still having trouble figuring out how to get non-CRUD operations into a RESTful API.

Here's what I'm trying to do specifically. We're implementing an API to manage DomainKeys (link).

Let's say you fetch a list of your domain keys like this.

Code: Select all

GET http://example.com/domainkeys/


You could get information about a single domain key like this.

Code: Select all

GET http://example.com/domainkeys/1234/


That will contain information like the RSA key, the domain being authenticated against, and whether that domain has an appropriate DNS record. All three of these values are stored in our database. Implementing all the basic CRUD operations for this is a piece of cake. I'm having trouble with how we would make a RESTful API call to verify the DNS record.

My first instinct would be to make a url like this.

Code: Select all

http://example.com/domainkeys/1234/verify


However, this is very clearly not RESTful. What kind of resource is represented by "verify"? Well... none. Verify is an action we'd like to perform, not a resource we would like to act on.

We've basically got these four verbs to work with: GET, POST, PUT, DELETE. DELETE is clearly out, as we don't want to delete the domain key. GET is out, because we're not allowed to alter the resource via GET. That leaves POST and PUT, but that's about as far as I've gotten with this line of thinking.

User avatar
Steax
SecondTalon's Goon Squad
Posts: 3038
Joined: Sat Jan 12, 2008 12:18 pm UTC

Re: RESTful APIs and non-CRUD operations

Postby Steax » Fri Oct 08, 2010 10:05 am UTC

Disclaimer: I'm no expert or academic in this subject. I usually do things that play well and work out practically, so it might not be the 'real' solution.

My rule of thumb between choosing between GET and POST is that GET retrieves something with no major effects on the data. I.e. reading stuff. Otherwise, it's POST. What I would do is perform a POST to http://example.com/domainkeys/1234/ with a parameter (say 'query') set to 'verify'. I'm not sure if that's the valid way of doing it, though. (This is one of those confusion problems you get with an architecture like REST with no formal protocol.)

Alternately, you can call http://example.com/domainkeys/1234/verify/ and say that you're calling upon the 'verification system' as a resource within that domain key. Personally, in terms of 'making personal sense', I'd do that. Piling too much meaning on the 1234 resource starts stretching it. I don't really think this is the 'correct' method, however.
In Minecraft, I use the username Rirez.

User avatar
b.i.o
Green is the loneliest number
Posts: 2519
Joined: Fri Jul 27, 2007 4:38 pm UTC
Location: Hong Kong

Re: RESTful APIs and non-CRUD operations

Postby b.i.o » Fri Oct 08, 2010 2:00 pm UTC

Pesto wrote:My first instinct would be to make a url like this.

Code: Select all

http://example.com/domainkeys/1234/verify


However, this is very clearly not RESTful. What kind of resource is represented by "verify"? Well... none. Verify is an action we'd like to perform, not a resource we would like to act on.

I think this is fine, because the resource you're acting on is the thing referenced by the URL /domainkeys/1234, and "verify" is simply an action you're performing on that resource. If you're working in something like Rails, you'll have /resource/id/update, /resource/id/create, and /resource/id/delete too, which are all structured similarly.

That leaves POST and PUT, but that's about as far as I've gotten with this line of thinking.

There are lots of POST vs. PUT explanations on the internet that I won't try to summarize for you, but I've always found this (somewhat simplified) explanation helpful:

One important difference between PUT and POST is that the former is idempotent: if you call PUT on an object multiple times it won't have any effect. That is, you should use PUT for actions that completely create or overwrite the resource at the URL you're calling. POST, in contrast, is best used for updating a resource or causing a change. Using a very simplified view, an action like "x = 0" would be a PUT, and an action like "x++" would be a POST.

One important observation, then, is that you can use the same URL for multiple kinds of actions when you're using a PUT request, because the PUT request is idempotent. So you can have /resource/id both create and modify an object, depending on if /resource/id already exists or not. So (depending on how everything works, exactly) you could conceivably have the URL for verifying be the same as the URL for getting a specific domain key, just with a different kind of request attached.

User avatar
Pesto
Posts: 737
Joined: Wed Sep 05, 2007 5:33 pm UTC
Location: Berkeley, CA

Re: RESTful APIs and non-CRUD operations

Postby Pesto » Fri Oct 08, 2010 2:56 pm UTC

If I were to be entirely anal about this, I'd probably have to use POST in some form, because I'm not sure the verify call would be idempotent. The verify call checks the validity of some outside resource, so there's no guarantee that multiple calls will result in the same resource state. For example, the first call could successfully verify the DNS record, but on the second call there could be a networking hiccup and the verification would fail, not being able to find the DNS server at all.

So it looks like the best strategy would be to POST to http://example.com/domainkeys/1234, but what if there are other similar non-CRUD actions that I would want to do over POST? How would I differentiate them?

User avatar
b.i.o
Green is the loneliest number
Posts: 2519
Joined: Fri Jul 27, 2007 4:38 pm UTC
Location: Hong Kong

Re: RESTful APIs and non-CRUD operations

Postby b.i.o » Fri Oct 08, 2010 8:33 pm UTC

Pesto wrote:So it looks like the best strategy would be to POST to http://example.com/domainkeys/1234, but what if there are other similar non-CRUD actions that I would want to do over POST? How would I differentiate them?

By using different URLs. I'm really not sure why you're against just doing http://example.com/domainkeys/1234/verify. REST and CRUD aren't the same things, and you can do things RESTfully without having them be CRUD operations.

User avatar
Pesto
Posts: 737
Joined: Wed Sep 05, 2007 5:33 pm UTC
Location: Berkeley, CA

Re: RESTful APIs and non-CRUD operations

Postby Pesto » Fri Oct 08, 2010 9:08 pm UTC

What resource would http://example.com/domainkeys/1234/verify represent?

Edit: If the representation of a domain key looks like this...

Code: Select all

RSA: blah blah...
Domain: foo.domainkey.example.com
DNS status: No DNS record found

Perhaps we could do it with a POST against http://example.com/domainkeys/1234/dnsStatus

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: RESTful APIs and non-CRUD operations

Postby Ubik » Sat Oct 09, 2010 8:05 am UTC

I'd make that GET instead of POST, because the request isn't about modifying a resource. Its results might indicate that a resource is in different state now than earlier, but it isn't causing the change.

User avatar
RoadieRich
The Black Hand
Posts: 1037
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Behind you

Re: RESTful APIs and non-CRUD operations

Postby RoadieRich » Sat Oct 09, 2010 12:46 pm UTC

I'm nothing like an expert in REST, but would it make sense to do something like
POST http://www.example.com/domainkeys/verifier
with key=1234 in the postdata (or
GET http://www.example.com/domainkeys/verifier/1234
)? The resource you're accessing is the system to verify a domain key (or the verification status of the 1234 domainkey) - which doesn't really fit into the key's structure.

This could be against the REST API spec, but it seems to make sense in terms of system layout.
73, de KE8BSL loc EN26.

User avatar
Pesto
Posts: 737
Joined: Wed Sep 05, 2007 5:33 pm UTC
Location: Berkeley, CA

Re: RESTful APIs and non-CRUD operations

Postby Pesto » Sat Oct 09, 2010 7:46 pm UTC

Ubik wrote:I'd make that GET instead of POST, because the request isn't about modifying a resource. Its results might indicate that a resource is in different state now than earlier, but it isn't causing the change.

Except it is about modifying the resource. dnsStatus is a value stored in the database. Posting to that resource would tell the server to look up the DNS record and store the most current information.

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: RESTful APIs and non-CRUD operations

Postby Ubik » Sun Oct 10, 2010 8:12 am UTC

I had the impression that the DNS status thing would be something for which the server acts only as a kind of proxy. Something like this: client asks for DNS status -> server does a DNS query -> gets a result -> interprets it (-> possibly caches the result) -> returns the result to client. If the value is stored into the server's database but only for caching purposes, I would still make it a GET.

robble
Posts: 2
Joined: Fri Nov 13, 2009 9:27 am UTC

Re: RESTful APIs and non-CRUD operations

Postby robble » Fri Feb 07, 2014 8:23 am UTC

I know this is an old thread, but I thought it might be worth clarifying in case a nascent RESTful API designer stumbles across it.

1) If you're not updating anything, use GET.
2) The request IS idempotent, even if there is a network failure. Otherwise nothing real could be considered idempotent.
3) See 1).

I think RoadieRich's idea is a good one: GET /domainkeys/verifier/1234

The point of REST is to not create infinite verbs, but to stick with the 4 or so provided. So creating a VERIFY verb isn't RESTful; creating a noun (e.g. verifier) that we can GET stuff from definitely seems more RESTful.

User avatar
Steax
SecondTalon's Goon Squad
Posts: 3038
Joined: Sat Jan 12, 2008 12:18 pm UTC

Re: RESTful APIs and non-CRUD operations

Postby Steax » Fri Feb 07, 2014 8:54 am UTC

Wow, I was really different 4 years ago, but I still stand with what I said last time.

Most RESTful APIs follow this general pattern:

Code: Select all

GET     /domainkeys                     List all keys
GET     /domainkeys/create              Show form to create new key
POST    /domainkeys                     Store new key
GET     /domainkeys/[id]                Show key [id]
GET     /domainkeys/[id]/edit           Show form to edit key [id]
PUT     /domainkeys/[id]                Replace existing key [id] with new data
PATCH   /domainkeys/[id]                Update existing key [id] with any supplied fields
DELETE  /domainkeys/[id]                Destroy key


So I think it's still sane to continue with it for practicality's sake:

Code: Select all

GET     /domainkeys/[id]/verify         Show form to verify key (if necessary)
POST    /domainkeys/[id]/verify         Verify key


If anyone does stumble across this, though, the "correct" answer is probably just "pick a solution and document it thoroughly".
In Minecraft, I use the username Rirez.

lgw
Posts: 437
Joined: Mon Apr 12, 2010 10:52 pm UTC

Re: RESTful APIs and non-CRUD operations

Postby lgw » Fri Feb 07, 2014 9:20 pm UTC

I recently helped convert a pretty large API to a RESTful API. The nice thing is: examples like "verify" were quite rare. There are very few interactions with a server where you want to synchronously perform some action that isn't "update the property to this new value", but this thread is a handy example of the exceptions, so thanks to the OP for pointing it out!

For the sake of that hypothetical future RESTful developer, let me add that there are far more asynch (long running) actions you might want the server to perform, at least the sort of software I work on. REST fits those operations quite well! Just create some sort of task/job model. POST to start a long running action (create a task), GET to get the status of one/all tasks. DELETE to cancel a task.

If idempotency of adding tasks is important, the client should supply an ID in the POST that's opaque to the server (not the task ID used to GET/DELETE the task, because clients will conflict) that's used only for this purpose: blocking duplicate task submissions due to error/retries.
"In no set of physics laws do you get two cats." - doogly


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 7 guests