Alan Dean

CTO, Developer, Agile Practitioner

Photograph of Alan Dean

Friday, November 7, 2008

What a RESTful Basket Checkout might look like

In my previous blog post, I discussed "When Basket Checkout isn't RESTful" so here are some thoughts on what a RESTful implementation might look like.

This is what I want to have as the final step of transitioning to checkout (where application/basket+xml is a standard media type):

POST /checkout
Content-Type: application/basket+xml

<basket xmlns="http://example.org/basket">
<item id="1" count="1" product="http://example.com/product/green-ham" />
</basket>

This is deliberately trivial xml as the format itself is not especially important. What is noteworthy is that the basket contains all of the items that the user wants to purchase. The basket state might have been constructed without ever previously visiting the server, perhaps in a rich desktop client.

Upon receipt of the basket xml, the server sends a WWW-Authenticate challenge to authenticate the user. If the authentication succeeds, it continues with whatever workflow it has for checkout.

Note how this request fulfils the REST constraint to manipulate resources through the exchange of representations (in this case, of a user basket).

After checkout, the server would likely expose the current state of the order to the authenticated user, for example:

GET /order/abc123
Content-Type: application/order+xml

<order xmlns="http://example.org/order" total="12.34" xml:base="http://example.com/order/abc123" currency="GBP">
<item id="1" count="1" product="http://example.com/product/green-ham" price="12.34" />
<payment>...</payment>
<delivery>...</delivery>
</order>

OK, so now you can see what the end objective looks like, it makes the client side journey a little easier.

If the client is a rich client, life is simple: just store the basket state in memory or persist it to a local data store as desired. When the time comes to checkout, the necessary basket xml is crafted and then sent to the server using something like HttpWebRequest. All runtimes expose the functionality to send HTTP messages these days; read your documentation for guidance.

If you are in the browser then you will need to store the data locally as well. There are a number of alternatives. The most obvious is in a client cookie, but you could also use Google Gears if it is installed.

In my previous post, I discussed the proposal by serialseb for a 'personalized resource'. Purely in context to the client needing to store the basket state, this makes sense. You might, for example, have requirement to remember the last basket state in order to highlight previous purchases. What is imperative, however, is that only the client has knowledge of this 'personalized resource'. At no time does the server which will checkout the basket ever know about where the basket state is persisted. In fact, this is an excellent example of meeting the Layered System constraint of REST and applying information hiding.

For my money, that is a RESTful Checkout.

8 comments:

mamund said...

looks like your approach is to make sure the client has a full representation of the basket before being allowed to check out. this is fine asl long as the basket is small enough to fit into available client-side storage (cookies, gears, etc. however, it fails if the full rendering of the basket is very large.

i prefer to allow the client to keep a pointer to their stored basket and 'move' that baste from 'shopping' to 'checkout' stage by posting the pointer to a new URL.

Alan Dean said...

Mike,

In my opinion, you propose the same design (roughly) as serialseb did. In my previous post I explain why I do not consider this RESTful, which can be boiled down to:
1. You aren't exchanging a self-descriptive representation of the resource
2. The server has knowledge of the application state which breaches the Layered System constraint even if you are correctly stateless.
3. It's all too easy for this approach to fail the stateless constraint; if not in version, then by version 2.

Valid HTTP, but not REST.

Besides, if size is a problem (which I can understand with a client cookie, but not Gears), in this post I propose an alternative to that pattern that is RESTful and doesn't require physically local storage but does provide logically client storage.

mamund said...

Alan:

first, are you most interested in talking about *storing* state or *changing* state?

on storage:
i store blog posts, comments, to-do lists, lots of things, on servers for retrieval later. does that make all those servers non-REST?

on changing state:
if i post a document that authorizes the server to send an email to a pre-built set of friends, this changes the state of the application, right? so is this a non-REST-ful app?

i want to make sure i understand the step(s) the you find break the REST constraints.

mamund said...

Alan:

a couple more thing crossed my mind as i re-read your post:

1 - is it possible to change the state of a REST-ful application just by following a link?

2 - does sending a DELETE to an application constitute changing that application's state?

in other words, do all state changes for a REST-ful app require sending a payload to the server? if not, then why does moving a basket from the "pending" to the "checkout" state require sending a payload to the server?

i'll stand down for a bit.

Alan Dean said...

Mike,

It is important to differentiate between (client) application state which the server should know nothing about and (server) resource state.

In answer to Q1: Yes and No. You can change the application state by following a link so you are simply keeping a reference to your 'currently selected steady state'. You can't change resource state by following a link, however - to do that you must pass a representation to the server.

In answer to Q2: DELETE requests that the server destroy the resource state (though the server is free to do a logical delete operation if it so chooses). Therefore having no payload is entirely appropriate.

Stefan Tilkov said...

Alan,

I have no idea whether you are still checking comments here, but …

One can argue whether sending a link is the same as sending a representation, but that's beside the point IMO. Instead of sending a link to the basket/cart kept on the server, the client could GET it and then POST that complete representation to the checkout resource.

The real issue to me is how you build up the cart over the course of multiple requests. I see no problem doing the client-side solution you propose, but this may not always be an option. It seems perfectly RESTful to me to create a shopping cart resource on the server via POST, amend it by adding items to it via POST, and then either GET and POST it, or POST a link, to the checkout. The gradual creation of the cart and the checkout are decoupled. I see no REST constraint being violated.

Am I missing something?

Alan Dean said...

Stefan,

I agree that you can build up the cart in the manner you describe by using POST.

My objection is when the checkout isn't driven from a representation, but instead goes 'behind the scenes' to retrieve cart state (usually reading it directly from the database), thus directly coupling the cart storage and checkout. Sadly, this anti-pattern is the standard e-commerce implementation currently.

Stefan Tilkov said...

OK, understood.

I think this is a specific case of the general scenario where resources are coupled behind the scenes. If they're part of the same "service" (insert your favorite term here), that's fine; if they're not, it violates the idea of loose coupling.

In other words: If I define cart and order processing as being part of the same application or service, I can use the approach you criticize. If I aim to design them so they're independent, your alternative is better.