June 16, 2006

PUT vs POST

Update: here's a good thread from the rest-discuss list in April on this.

In reading up on REST principles and arguments over the last couple of weeks, I've run into quite a bit of confusion about what some of the HTTP methods mean. GET and DELETE are straightforward, no confusion. But PUT and POST are another matter.

For instance, in his excellent article How to Create a REST Protocol, Joe Gregorio has the following table:

HTTP Method CRUD Action Description
POST CREATE Create a new resource
GET RETRIEVE Retrieve a representation of a resource
PUT UPDATE Update a resource
DELETE DELETE Delete a resource

While it's useful on the surface to think about mapping HTTP verbs to SQL verbs, I think that because there's overlap between PUT and POST we're hiding an important impedance mismatch between data-as-relational-records and data-as-manipulatable-resources. For instance, the table implies that PUT is like the SQL UPDATE, but by common acceptance PUT shouldn't be used to just update 1 field of 20 as commonly done with SQL UPDATEs. So when you PUT an update with just a change of a customer's first name and a number of other fields are modified (because you didn't supply values for them), you might get a little freaked out.

Here's a similar view from Paul Prescod, who says in his Common REST Mistakes:

Do not overuse POST. POST is in some senses the "most flexible" of HTTP's methods. It has a slightly looser definition than the other methods and it supports sending information in and getting information out at the same time. Therefore there is a tendency to want to use POST for everything. In your first REST Web Service, I would say that you should only use POST when you are creating a new URI. Pretend POST means "create new URI as child of the current URI." As you get more sophisticated, you may decide to use POST for other kinds of mutations on a resource. One rule of thumb is to ask yourself whether you are using POST to do something that is really a GET, DELETE or PUT, or could be decomposed into a combination of methods. (emphasis added)

To muddy the waters even more, here's what the HTTP 1.1 RFC says about POST:

POST is designed to allow a uniform method to cover the following functions:
  • Annotation of existing resources;
  • Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles;
  • Providing a block of data, such as the result of submitting a form, to a data-handling process;
  • Extending a database through an append operation.

...and about PUT:

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

It's easy to see where the confusion is because according to the spec:

  • POST can be used as an UPDATE (annotating an existing resource) and as an INSERT (posting a message to a bulletin board)
  • PUT can be used as a full UPDATE (replacing entirely an existing resource) and as an INSERT (adding a new resource)

So when you're using HTTP methods as an integral part of your application it's important to clarify how you're interpreting PUT and POST.

Here's my first attempt:

  • PUT is create or replace
  • POST is partial or complete update of an existing resource, or adding metadata to an existing resource

Some examples. Add a new customer:

PUT /customer HTTP/1.1
Content-Type: text/xml
<customer>...</customer>

Update all fields of customer with ID 1234:

PUT /customer/1234 HTTP/1.1
Content-Type: text/xml
<customer>...</customer>

Update the 'firstName' field of customer with ID 1234:

POST /customer/1234 HTTP/1.1
Content-Type: text/xml
<customer>
  <firstName>Napoleon</firstName>
</customer>

That's easy. Now, for something more complicated: what if I want to add a new address to a customer? I could do it with either one depending on how the resources are structured. Is an address a separately addressable resource, or is it completely dependent on a customer? Can there be more than one address of a particular type, or can a customer have as many addresses as necessary?

It's probably a good rule of thumb to make every resource separately addressable. You never know how you might want to use them in the future. So we'll use a PUT and include a link to our related resource:

PUT /address HTTP/1.1
Content-Type: text/xml
<address>
  <street-address>111 Main Street<street-address>
  <city>Pittsburgh</city>
  <state>PA</state>
  <postal>15217</postal>
  <customer href="/customer/1234" />
</address>

That seems to work pretty well. There may be cases I haven't thought of yet, we'll see how they can fit into this scheme.

Next: Another phrase works its way into the consciousness
Previous: Considering REST