RESTful Webservices module, an Ode to Drupal and Open Source

This is an Ode to the RESTful Webservices Project (http://drupal.org/project/restws) on Drupal.org with some instructions on how to actually create nodes, or any other entity type (users for example).

We were about to embark on upgrading all our webservices to Drupal 7, when i started researching what has changed in the Drupal restful services space. When i created the site for Babson about 2 years ago, web services was in a useful state, and not much else dotted the landscape. Low and behold i found the RESTful Webservices Project and believe it or not you just install the module, no configuration, no coding, and anything that's an "entity" in Drupal, ie a node, a user, etc. is available for CRUD operations using POST, PUT, DELETE or GET. BAM! HOLY CR_P BATMAT, now that is the power of Drupal and a case study of how Drupal and open sourced saved Babson 3 weeks of development work. 3 weeks of work replaced by 1 day of figuring out how to use restws (the drush name for this new rest project). Nice.

I may craft poems and Hobbit songs about this module, Klausi, and sepgil who authored it, I'm now sold on using entity_ids in the database as a standard for foreign keys -- that's what makes creating web services dynamically possible. There is documentation here for the module (this wiki post does not replace your need to read it): http://drupal.org/node/1860564 and I recommend the short video from the page above http://www.youtube.com/watch?v=Mr4jwnvUyaU but there was no complete (with examples) documentation for doing creates or updates to nodes (or other entities), so I spent a half day hacking away at getting it working. The readme was essential http://drupalcode.org/project/restws.git/blob/refs/heads/7.x-2.x:/READM… I eventually got it working by changing line 456 of restws.module to (yes I changed it back after I figured out what I was missing)

if (user_is_logged_in() && empty($_COOKIE[session_name()])) {

from what it was supposed to be

if (user_is_logged_in() && !empty($_COOKIE[session_name()])) {

and if i didn't change the line I'd get 403 forbidden errors. I traced through the code further and figured out how to set the X-CSRF-Token, and stumbled upon Klausi's release notes http://drupal.org/node/1890216 and then noticed in the readme I had missed the line warning I would need to set this token to avoid CSRF hacks. My bet is many are struggling with the same trying to get POSTS (creating new nodes -- or any other entity) and PUTS (updating nodes -- or any other entity)working and so here is a working example of creating a node with restws module.  This is hardly a criticism of the brilliant work done here in restws, consider this a lyrical poem from a software developer who considers there to be beauty in how he can now easily have web services by only installing a single contrib module.  

Ode to Drupal and open source, and saving thousands of dollars per project due to community code.  Let this documentation of restws be a geeky Hobbit song in honor of this fine module which allows me to succeed and enjoy the break room and a couple more social conversations instead of 3 weeks of hard work, and of course the module allows me to also move on to all the other work to do in the Dwarven mines  -- less stress, more productivity.  After a slightly longer lunch I can now turn to face the upgrading of the database all the quicker.

NOTE:  When using curl you should only go to https not http, the example below goes to http (non secure) only because it is on my local machine.

 

echo 'hello';  
$url = "http://bell7:8888/node";

// This is example data from when you do a get, not needed for a post to create
// a node.  You can do a get of data from any entity by going to
// http://bell7:8888/node/1.json  which is the same url to view the node for
// example but you stick the .json onto the end.  Below is example output I used
// to construct the $new_node array below.
/* {"body":{"value":"\u003Cp\u003EJSON Group\u003C\/p\u003E\n","summary":"","format":"filtered_html"},"group_group":true,"field_end_date":"1425495600","field_start_date":"1362337200","members":[{"uri":"http:\/\/bell7:8888\/user\/1","id":"1","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/2","id":"2","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/23","id":"23","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/22","id":"22","resource":"user"}],"members__1":[{"uri":"http:\/\/bell7:8888\/user\/1","id":"1","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/2","id":"2","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/23","id":"23","resource":"user"},{"uri":"http:\/\/bell7:8888\/user\/22","id":"22","resource":"user"}],"members__2":[],"members__3":[],"nid":"1","vid":"1","is_new":false,"type":"group","title":"First Group","language":"und","url":"http:\/\/bell7:8888\/node\/1","status":"1","promote":"1","sticky":"0","author":{"uri":"http:\/\/bell7:8888\/user\/1","id":"1","resource":"user"},"log":"","revision":null,"comment":"1","comment_count":"0","comment_count_new":"0","views":0,"day_views":0,"last_view":null} */

$new_node = array(
      'title'     => 'hello from restful services',
      'body' => array(
        'value' => 'hi there',
        'summary' => 'hi there', 
        'format' => 'filtered_html'
      ),
      'comment'   => 2,
      'promote'   => 0,
      'revision'  => 1,
      'log'       => '',
      'status'    => 1,
      'sticky'    => 0,
      'type'      => 'group',
      'language'  => 'und',
      'author'    => 1,
    );
$json = json_encode($new_node);

$result = add_node($json);

function add_node($post_data){
  //global $user;
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"http://bell7:8888/user/login");
  curl_setopt($ch, CURLOPT_HEADER, 0);
  curl_setopt($ch, CURLOPT_USERAGENT, 'PHP script');
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  curl_setopt($ch, CURLOPT_COOKIEJAR, "session_cookie.txt");
  curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt');
  curl_setopt($ch, CURLOPT_POST, TRUE);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "name=username&pass=your_password&form_id=user_login");
  curl_setopt($ch, CURLOPT_VERBOSE, true);
  curl_setopt($ch, CURLOPT_COOKIE, session_name() . '=' . session_id());
  ob_start();  //dont print out this output
  curl_exec ($ch);
  curl_setopt($ch, CURLOPT_URL, "http://bell7:8888/restws/session/token");
  $HTTP_X_CSRF_TOKEN = curl_exec($ch);
  ob_end_clean();  //resume printout output to screen

  curl_setopt($ch, CURLOPT_URL, "http://bell7:8888/node");
  curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // output to command line
  curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'X-CSRF-Token: ' . $HTTP_X_CSRF_TOKEN));

  $ret = new stdClass;
  $ret->response = curl_exec($ch); // execute and get response
  $ret->error    = curl_error($ch);
  $ret->info     = curl_getinfo($ch);
  print_r($ret);

curl_close ($ch);

unset($ch);

return;
}

The restws module also supports returning more than one record, & it supports filtering, sorting and "direction" which works like order by does in MYSQL. This code for example on my local machine will get my user information (filtering by username):

$url = "http://bell7:8888/user.json?name=jbarnett";
$result = get_node($url);

function get_node($url){
  //global $user;
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"http://bell7:8888/user/login");
  curl_setopt($ch, CURLOPT_HEADER, 0);
  curl_setopt($ch, CURLOPT_USERAGENT, 'PHP script');
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  curl_setopt($ch, CURLOPT_COOKIEJAR, "session_cookie.txt");
  curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt');
  curl_setopt($ch, CURLOPT_POST, TRUE);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "name=username&pass=password&form_id=user_login");
  curl_setopt($ch, CURLOPT_VERBOSE, true);
  curl_setopt($ch, CURLOPT_COOKIE, session_name() . '=' . session_id());
  ob_start();  //dont print out this output
  curl_exec ($ch);
  curl_setopt($ch, CURLOPT_URL, "http://bell7:8888/restws/session/token");
  $HTTP_X_CSRF_TOKEN = curl_exec($ch);
  ob_end_clean();  //resume printout output to screen
  curl_setopt($ch, CURLOPT_HTTPGET, TRUE);
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // output to command line
  curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'X-CSRF-Token: ' . $HTTP_X_CSRF_TOKEN));

  $ret = new stdClass;
  $ret--->response = curl_exec($ch); // execute and get response
  $ret->error    = curl_error($ch);
  $ret->info     = curl_getinfo($ch);
  // uncomment the below line to get more verbosity on response;
  print_r($ret);

curl_close ($ch);

unset($ch);

return;
}