views:

72

answers:

2

I'm designing a REST interface and I'm puzzled by what should the insert/update/delete verb return (as content of the response). Consider an inteface for Invoice entities, accessible at api/invoices:

  • GET /api/invoices returns a list of invoices
  • GET /api/invoices/123 returns the invoice with ID 123
  • POST /api/invoices adds a new invoice (ID generated on server)
  • POST /api/invoices/123 updates invoice with ID 123
  • DELETE /api/invoices/123 deletes invoice with ID 123

Its pretty obvious what the first two method should return, but how about the insert/update/delet ones? An obvious answer is that add should return the newly created item (ie. exactly the same response as GET /api/invoices/id), update should return the updated item (again, same as a GET) and delete should probably return nothing (empty content). This all makes sense and is consitent.

But my problems start when considering items that are not so simple as an Invoice entity. For instance consider an add request that not only adds the item, but actually needs to return some extra information about the add operation: the item was accepted (success), the item is a duplicate (success with info), the item was ignored (success with info, I won't eneter into details why), the item was rejected (failure). Also in my case there is extra information I want to return, like a 'response' info pre-set for the bucket into the newly added items falls. I considered placing all the extra information in the http headers as sort of out-of-band info (like extra status codes in the 200 range and even custom headers) but is a hack, and the 'response' part can be actualy larger than the item itself. So now I'm considering the add verb to return a completely new type, an item that contains the add info (status, response, ID of the new item, perhpas the entire new item). It certainly gets the job done, but I miss the nice symmetry I had before.

Would this be a good practice (when adding an item of type 'foo' the return is of type 'bar') or is something I'll look back in 6 month and pull my hair because I let the cat out of the bag? If I stick with 'any access on foo returns foo, including add', then the client would have to make an extra call after the 'add' operation to retrieve the information it is really interested in (ie. the 'response').

A: 

Your reply should be rather complex. It should not match the inbound request. We use JSON and we have some additional information in addition to the object.

Typical Reponse:

[{ "ID": the generated ID, "TYPE": the actual class name, "OBJECT": { the object } }]

POST should return the object that was created. If you have extra information (which isn't a good idea) you can carry that in the response message.

PUT should return the result of the update.

DELETE, like GET, can just returns a status.

Extra information in the status is not a hack -- that's why the status codes are so open-ended.

  • the item was accepted (success) - status is 200 OK

  • the item is a duplicate (success with info) - I'm not sure what possible info you would have above and beyond the final object created, but this is a place for 20x - OK WITH INFO status.

  • the item was ignored (success with info, I won't eneter into details why) - this is ambiguous. I wouldn't call it success, I'd call it a 40x - IGNORED. You you can call it a 20X - IGNORED if you can't go into the details why.

  • the item was rejected (failure). This is a plain old 40X REJECTED message.

S.Lott
My 'hack' comment was about adding custom HTTP headers. For HTTP statuses I agree with you totaly.
Remus Rusanu
A: 

An option to consider for the POST returns is to have it return the extra information and a link to the newly created object. This runs a nice midline between returning the same thing that GET would return and returning a completely distinct entity.

D.Shawley