REST REST REST REST REST
So some of you may have heard about RESTful interfaces. What I am going to describe here is my vague interpretation of REST through a web developers glasses, with respect to Catalyst and ExtJS. But first some background.
I am working on a relatively new project at work (6 months as opposed to 10+ years) and I’ve been striving to use the best tools for the job through and through. I was initially going to try for Rails, but fortunately my boss curbed that interest by saying no, (subtly!) So instead of using Rails I looked at Catalyst briefly and I already knew CGIApp from years before. Catalyst seemed like a no-go as there had previously been issues with it and windows at our work, so CGIApp seemed like the best route to take. It was still light years better than a giant bag of non-persistent CGI scripts with extremely little code reuse.
Next up is the front-end design. Say what you will about requiring users to use Javascript; if it’s an internal website, I see no reason not to do a pure Javascript site as long as you can at least try to pull off 508/ARIA. And if a website is pure Javascript, with just enough html for a single div and a header, it is probably using ExtJS. There may be other ways to pull it off, but Ext really is pretty nice to use. I very rarely have browser compatibility issues with Ext, whereas with prototype it was almost bare metal. So with Ext, instead of previously, where we had a giant bag of standalone scripts, we have been doing an inheritance based (and prototypical, duh) OO interface. It’s been excellent for UI design and code reuse. I think that some of the things we have done with Ext rival UI design I could do (as a mere programmer who’s read a couple books about UI design) with any other framework, and that includes flash and non-web GUI’s. It’s very elegant due to Javascript’s design (events, OO out of the box, etc).
Now that we have theory out of the way, lets talk more concrete issues. For Ext you have a Grid class. You can think of a Grid as basically somewhere between a table and a spreadsheet, plus lots of other sexy goodies. To set up an Ext Grid you need a store and a column model. Really the meat of the Grid is in the column model, so I made a subclass of the grid that generates the store automatically based on an action and a controller. This is really helpful because basically all of your dispatching is done in one place. That means if I change my server side dispatching I only have to change one thing in the javascript (and I have done this.)
I also add a bunch of CRUD-y buttons to the top toolbar of the grid. And of course, because they are generated automatically I generate the urls for Creating, Reading, Updating, and Deleting these records automatically. I define controller and action (currently this compiles to /controller/action, it used to be /Controller_controller/action…whatever) and I define server side actions for create_action, single_action, update_action, and delete_action. It works fairly well, except when I’m dumb and call action something like single_report. Then I end up with reasonable things like, delete_single_report…and silly things like single_single_report. Yeah that happened.
Recently I was working on a really basic Grid. The user won’t even have windows for the rows as usual, and will instead edit the Grid directly. One of my coworkers has been working on something like this lately anyway, so I asked him how he did it and went at it. It involved a REST based Ext store, a per-row editor for the grid, and the server has to support REST somehow. So far so good.
I implemented my Ext class by copying my original Ext subclass. I didn’t subclass it because this is slated to replace it. I added a temporary dispatching feature to it so that instead of dispatching to /controller/action it will dispatch to /controllerREST/action. That way I will slowly migrate things from my ghetto dispatching to REST and then when I want to switch everything at once I just move the controllers and change the dispatching in my one subclass. Easy peasy.
For the Catalyst side (and I specifically waited till I finished the Catalyst switch for this) we use Catalyst::Controller::REST. At first this is a small hassle to set it. It’s (seemingly) an all or nothing setup, so instead of making single actions RESTful (possible but not as nice) you just make RESTful controllers. The Action class takes care of the basics of RESTful dispatching. That is, PUT’s get routed to /controller/action_PUT. The controller actually does some really sexy stuff. It will look at the headers sent from the browser (Accept) to see what format the message sent across the wire was, automatically decode it, and put it in $c->request->data. You do whatever you want based on that and then you put data in (manually or using a helper function) $c->stash->{rest}, and it again uses the Accept header to decide how to format the data.
Now you may have missed it, but this makes it stupid easy to have your server support JSON, YAML, XML, and a bunch of other serializers baked in for free! Well, as long as you can set the Accept header. (I set it with a global override in Ext.) But more importantly for me it makes it quite clear what my interface is, and a simple way of automating the usage of that interface. Instead of adding single or update to some url, I just do a PUT for creation, or a DELETE for deletion. It’s simplified our server side and client side code significantly!
Also note: using REST you can simplify a lot of other things, like instead of everything being a text blob, you can have integers, booleans, and other complex data structures. This means that I can have non sucky checkboxes. This is a big win.
Now that I’ve finished writing and editing this blog post it is a whopping 1059 words long, so I should probably stop there. But I may blog more about this REST stuff in the future. What about you? Do you use a RESTful interface? Do you hate REST? Why?
Posted Fri, Jul 24, 2009If you're interested in being notified when new posts are published, you can subscribe here; you'll get an email once a week at the most.