For the latest iteration of one of our bigger client projects, I was tasked with creating both an RSS feed and a semi-RESTful XML service to allow authorised outside sites to pull in and display data that is useful to them.
Drupal is fantastic in that functions like node_load() handle things like complex relationships, and essentially present you with a ready-made resource to parse and present as data to anyone requesting it.
XML and me
First of all, there were two separate things to develop - an RSS feed, and XML was the required return format for the data in this API, specified by the client.
Unfortunately, i’ve been spoiled by json_encode(), which turns lovely PHP objects into even lovelier JSON strings. Ruby on rails has a nice equivalent too, which uses the render :xml => resource function to output a hash table (similar to a PHP object) to XML. But I couldn’t find anything that was as simple for PHP (that wasn’t a pain to integrate with Drupal, and in the time provided).
It isn’t ideal, but at the same time, I think that writing the output like this gives you more incentive to be succinct and really think about what you’re doing. Frameworks are fantastic, but relying on them can lead to sloppy development.
The only thing you HAVE to do
Two words: Access Control
Remember that your API without access control might expose data that is normally behind a login screen.
Authentication systems on some APIs are unbelievably complicated, but understandable. I went with the simplest option I could think of: the good ol’ API key. This would allow us to present the key to logged-in users, that they could note down and use when interacting with the API.
Each key belonged to a node, whose IDs are unique, so creating a key was simply a case of using the md5 function around the node ID, with the time() and PHPs uniqid() function thrown in to increase the entropy a little more.
The whole key generation function looked like this:
$key = md5($node->;nid . time() . uniqid());
Simples! This was stored in a TEXT CCK field, then passed into the URL when a request was made:
http://example.com/api/xml/f7b7cfddb1a9fa300963bcb2e56b205a
The processing logic our custom module simply ran a query to backtrace the node ID that this key belonged to:
SELECT nid FROM {content_type_example}
WHERE field_authentication_key='f7b7cfddb1a9fa300963bcb2e56b205a';Routing requests
Again, here, Drupal’s got you covered! hook_menu() is the piece of magic that you need to build an endpoint:
/*
* Implementation of hook_menu()
*/
function custom_module_menu() {
$items['api/xml/%'] = array(
'title' => t('XML API'), /* Give The Page A Title */
'page callback' =>; 'xml_start',
'page arguments' => array(2),
'access callback' => 'xml_access',
'access arguments' => array(2),
'type' => MENU_CALLBACK
);
/* Return the items */
return $items;
}Don’t forget to clear your cache after making Menu Hook changes!
This basically tells Drupal to:
- Create a page at api/xml/ with the content returned by the xml_start() function
- Pass the third URL segment (marked with a %) to xml_start()
- Before showing the page, check if the user has access by running the xml_access() function, and also pass the third segment (which should be our API key) to the access function
The ‘access argument’ function will hold the logic to check that the API key supplied is valid, and will return true or false accordingly.
The xml_start function contains all of our logic to output the requested XML data, but instead of returning a value, and showing our drupal site in all it’s glory, including theme, should instead not return a value, but use the die; function to stop execution after you’ve finished outputting your XML. Also, don’t forget to set the correct content type header!
header('Content-type: application/rss+xml');
// Output your feed data here.
// Kill the script here.
die;And there we go! There’s no point in my showing the processing code because it’ll differ massively from site to site. But there we go, three steps to a working API endpoint that utilizes an API key.
A note on passing options
Options are pretty vital - they let you customise what is returned from the API, and for this project I just implemented GET options in the url:
api/xml/f7b7cfddb1a9fa300963bcb2e56b205a?limit=30&body=1
This lets you access them from the $_GET array from pretty much anywhere in your request, but beware - adding too many can seriously complicate the way your code flows, giving it a LOT of if() statements to handle the different arguments.
My best advice? Consider what’s really needed and whether they should be optional or just included by default. The great thing about an API is that the person utilizing it chooses what they want to use; it’s better that a piece of information is in there than not.
I’m lazy. Isn’t there a module for that?
There are of course alternatives, like the awesome services module, and a hundred and one views plugins for doing XML and RSS, but for this site, with such a custom, complicated data structure, writing our own was the best choice. And honestly? It was a great learning experience, and I was surprised by how easy Drupal can make the process of setting up and controlling authentication and application flow.
Try it! See what you can build.

Comments
Post new comment