The Wufoo REST Principles
On this Page
Other APIs
Introduction
In May 2010, we created a REST API for Wufoo to make it easier for developers and users to get at their data. This page describes the methodologies, decisions and justifications for the ideas we decided to implement in the new API. As our guiding light, we used RESTFul Web Services (RWS) by Leonard Richardson and Sam Ruby. In the book, Richardson and Ruby create a set of guidelines for creating RESTFul web services based on the concepts surrounding Resource Oriented Architecture. This page is basically a run-through of the RWS concepts they outlined in Chapter 4 of their book and how we designed our architecture within ROA’s bounds.
Since REST is not a specification, and contains some wiggle-room we’re using this document to work out and explain our REST choices. Please feel free to provide any feedback on our ideas, decisions and implementations. We hope to leave the discussion open for about 2 weeks, then commit to our final design.
Resources
In a Resource Oriented Architecture (ROA), the important piece of the system is a resource. For example, the most easily recognizable piece of information in Wufoo is the Form.
URIs
We use URIs to point to valid resources within a web service. According to RWS, the URIs you choose should have a few important properties. The first is descriptiveness. The URI should describe the content and have a structure that is predictable. We tried our best to lay out the URIs in a pattern that’s easy to follow, as shown below.
/users.xml
/forms.xml
/forms/{formId}.xml
/forms/{formId}/fields.xml
/forms/{formId}/entries.xml
/forms/{formId}/entries/{entryId}.xml
/forms/{formId}/entries/count.xml
/reports.xml
/reports/{reportId}.xml
/reports/{reportId}/widgets.xml
/reports/{reportId}/fields.xml
/reports/{reportId}/entries.xml
/reports/{reportId}/entries/count.xml
Remember, if you’re testing these URIs out, you must be either 1) logged into your subdomain or 2) provide your API credentials. Also, don’t forget to add your path prefix. So the Users API would read
http{s}://{subdomain}.wufoo.com/api/v3/users.xml?[pretty=true]
Relationship Between URIs and Resources
According to Richardson and Ruby, “a resource may have one URI or many… If a resource has multiple URIs, it’s easier for clients to refer to the resource. The downside is that each additional URI dilutes the value of all the others.”
The last sentence is where the author and I disagree. For example, in the listing of Wufoo’s URI pattern above, the {formId} can be replaced by either form hash, which is an unchanging value based on the form’s identifier, or the form URL, which corresponds to the form’s title, as defined by the user. In the example below, the the two URIs point to the same resource.
/forms/my-friendly-name.xml
/forms/xMr4u3.xml
I feel downside of a variable URI pointing to a single resource is overshadowed by the convenience offered to the user when browsing the API. Feedback on this option is welcome.
Addressability
Addressability is defined in RWS in the following way, “An application is addressable if it exposes the interesting aspects of its data set as resources.” You may think that if you’re using URIs to get to your data, than your application is addressable, but you can be wrong in that assumption.
An example of how you can get addressability wrong with respect to REST is when you set up a XML-over-RPC style API. For example, our last API had a single entry point. You’d pass an ‘action’ and some parameters to the entry point and you’d receive a response. While the entry point is addressable, you could not link to the individual portions of the system. Let’s make this more concrete with an example:
/forms/{formId}/entries.xml
The link above points to an individual entry within the Wufoo REST API. Given the proper credentials, you could link to this address, share it, put it an email, etc.
Now let’s look at how you’d get at the same data in our earlier, non-addressable system:
$wufoo_query_vals = array(
'w_api_key' => '1234-5678-9012-3456',
'w_version' => '2.0',
'w_form' => 'what-is-your-name',
'w_sort' => '1'
);
foreach($wufoo_query_vals as $key => $value) {
$request .= $key.'='.urlencode($value).'&';
}
$request = rtrim($request, '&');
$ch = curl_init("http://mashup.wufoo.com/api/query/");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
$response = curl_exec($ch);
curl_close ($ch);
Sure, the argument could be made that I’d still have to use CURL in PHP to get at the same information using an addressable system, but the the point is that CURL is not the only way I can reach this data.
We also found that Addressability can also help make your code more testable. Since each resource is represented by a URL, it is a snap to write test cases for your data using Selenium IDE.
Statelessness
Statelessness means, “that every HTTP request happens in complete isolation… the client should not have to coax the server into certain state to make it receptive to a certain request.”
We keep our REST API stateless when dealing with resources, but we still store some application state. I’ll explain the two ways in which we break the statelessness tenet.
The Wufoo REST API will hold your state if you make your request through the browser and you’re logged into Wufoo already. Requests made through your CURL client still require basic authentication, but we allow you to explore the API using your browser session. While this may not be helpful for the programmer when writing scripts, it does help when exploring the API before writing the scripts. In short, the choice to break statelessness was done to reduce the barrier to entry for API developers.
We also break statelessness when it comes to application throttling. We store the request count per API key and respond with a 421 error if you exceed your allotted requests. RWS warns about session replication and session affinity scaling issues. This is a real concern, but one which has been eased considerably when services like Memcached do the heavy lifting.
Representations
A resources is a representation of data. When you make a request to the Forms API, we send you back a list of all forms that you’re permitted to view. But how do we want to represent that data? We can make our own assumption about this data, and send you back an XML encoded list. But, this information could be represented by JSON, or as pretty-printed XML. Instead of making that choice for you, we provide different ways to represent your data and let you choose. Each format is a different representation of the data.
The author of RWS says it’s RESTFul to use HTTP’s Accept header specify the format of the response. However, it is also RESTFul to put this data into the URI. We chose the second method for added clarity and so that the API could be explored easily through the browser without having to hack the headers. This means that you can ask for
/forms.xml
or
/forms.json
or
/forms.xml&pretty=true
The first two are self-explanatory, but the last one deserves some attention. As stated earlier, our goal for the entire Wufoo REST API is to make uptake as easy as possible. As a result, we added the pretty=true parameter to allow the user to view the response in the browser.
In a normal request, we set the content type of the response to match the format type. But, if the pretty=true parameter exists, we set the content type to text/html, pretty print the output using Google’s free pretty print PHP code and wrap the entire response in a <pre> tag to make reading the data simple.
Links and Connectedness
This section deals with where and when to add hyperlinks to your returned resources. RWS states
The current state of the HTTP “session” is not stored on the server as a resource state, but tracked by the client as an application state, and created by the path the client takes through the Web. The server guides the client’s path by serving “hypermedia”: links… inside hypertext representations.
Using this as a guideline, we created link representations inside our returned resources. For example, if you call /users.xml, the resource returned looks like this (truncated) <User> element.
<User>
<User>imauser</User>
...
<LinkForms>http://fishbowl.wufoo.com/api/v3/forms.xml?pretty=true</LinkForms>
<LinkReports>http://fishbowl.wufoo.com/api/v3/reports.xml?pretty=true</LinkReports>
</User>
If you compare the elements <LinkForms> and <LinkReports> point the Wufoo API URI Pattern, you’ll notice that they match. Furthermore, the links inside of these elements are pre-formed URLs to those resources. So, someone traversing the API could simply follow the <LinkForms> element to the related forms for that user. Each <Form> element, in turn, points its own set of relative resources.
The decision to add links to the API was a hot topic of discussion within Wufoo. Those opposed to the idea based their argument on the intuitive feeling that these links wasted precious bandwidth without providing a defined benefit. In other words, why provide these links if one could simply find the answer required in the documentation? This argument was backed up by reading of Michael Bleigh’s excellent article on the topic REST isn’t what you think it is, and that’s OK, which made skipping these elements seem justifiable.
Ultimately, we decided to leave the links in because they provide an easy way to discover the API. One must only know one entry point - the Users Api - to navigate the entire API. As we move along with the Login API and add the POST and PUT verbs to the Entries Api, we’ll see if this connectedness is worth its salt.
The Uniform Interface
On this point we stuck with the doctrine. This concept boils down to the idea that one should use the HTTP verbs to gather and manipulate data through the API. For example, we use the HTTP verb GET to retrieve data and save POST, PUT, and DELETE for data manipulation.
This was, in fact, our greatest departure from our previous API implementation, which required an HTTP post for each request. In accepting the GET as our sole method of data retrieval, we freed ourselves up to make our system more addressable and our representations richer with the addition of the pretty=true parameter.
Conclusion
In conclusion, we built the Wufoo REST API to make things easier for our developers. We’re open to any suggestions or comments on our approach. Please provide your feedback.