Drupal Services Module

Déja Augustine's picture

Drupal is an extremely powerful and flexible content management system, and is a very strong foundation for a data-driven website. However, there are times when a solitary website just isn't enough -- for example you may want to have one main site and a number of affiliated sites, you may want to write a stand-alone application that can interact with your Drupal site, or you may want to share data between a Drupal site and a non-Drupal site. Situations like this call for a web service!

A web service is typically an API (application programming interface) that is hosted on a web server and exposed to the internet. One shining example of web services in our daily lives occurs every time you click the Submit button in an online shopping cart. A web service is responsible for telling the vendor that your credit card is valid and has enough room for you to make your purchase. Web services are also used to get your shipping quotes and reserve your tracking number.

The long and the short of it is that web services let websites talk to websites, websites talk with applications, and applications talk to applications.

Now, there are a number of different web service protocols (RPC, SOAP, AMF, REST, etc.). One of the drawbacks to web services is that they tend to be tied to a specific protocol, which means that you have to package up whatever data you wish to send to a web service in the protocol expected by the web service. If you are consuming multiple web services, it's quite possible that you'd have to package your data several different ways. To use the ecommerce example from above, you may have to package your order information (number of packages, each package's weight, source and destination addresses, etc.) for a SOAP-based shipping quote web service, and the customer's payment information for a REST-based credit card authorization service. This creates a lot of unnecessary complication in the world of web service consumption.

With all this in mind, when you sit down to write your own web service, where do you begin? What protocol should you use? How are you going to make the great behemoth of possibility that is Drupal available as a web service without sacrificing all that great power and flexibility by hard-coding your service to serve up only certain types of nodes and fields?

The beautiful answer is you don't have to think about any of that.

Enter the Services module. (http://drupal.org/project/services)

Services is a web service layer for Drupal that lets you check a few boxes and turn your Drupal website into a full-blown web service. In the Drupal tradition, Services is flexible and extensible. It separates the protocol used to transmit the data from the api that drives it. Out of the box, it contains the XML-RPC server module, but at the time of this writing there are modules to add JSON, JSON-RPC, REST, SOAP, and AMF compatibility. Each server module adds its own path. Changing protocols is as easy as changing the URL that the consuming application uses to connect. You can also easily write your own server module to define your own protocol or support one that does not already exist.

http://www.example.com/services/xmlrpc
http://www.example.com/services/json-rpc
http://www.example.com/services/super-awesome
etc.

The Server module also comes with a number of included services: node, comment, file, menu, search, system, taxonomy, user, views, and node_resource. Each service provides a number of methods, such as user.login, node.get, comment.save, taxonomy.selectNodes, etc. Using these services, you could write a stand-alone application that can sign in to your drupal site (user.login), read a node (node.get) and post a comment (comment.save) without ever having to crack open a web browser or parse a single line of HTML.

Just as with the protocols, adding your own services is very simple and straightforward. The Services module uses the protocol server module to parse the incoming data and then presents the data to your service module in a predictable, documented format. This means that you can write your custom service and not only will it be able to consumed simultaneously via SOAP, RPC, and SUPERawesome (your custom protocol), but it will also be able to consumed via protocols that have not even yet been defined.

In addition to the API, the Services module also provides a handy UI that lets you test any exposed methods by providing data directly into a form. Method help and argument description text can be provided by the service developer, and is displayed on this form.

Finally Services provides a number of options on the security front. If you don't feel comfortable opening up your website to any old web service, you can enable API Key Authentication which requires that a pre-created set of credentials be provided or your request will be rejected. You can also enable Session-based authentication requiring a valid session id to be presented with each call to verify that you're still the same consumer who connected/logged in previously. Finally, each service method can even provide its own access check so you could potentially have the ability to define which specific methods a particular role has access to.

I'll close out this exploration with a code sample showing you how little it takes to create a custom service module.

ExampleModule.info
name = "Example Service Module"
description = "Demonstrates creating a new service module"
package = "Services - services"
dependencies[] = services
core = "6.x"

ExampleModule.module

function ExampleModule_disable() {
        cache_clear_all('services:methods', 'cache');
}

function Example_enable() {
        cache_clear_all('services:methods', 'cache');
}

function ExampleModule_service() {
       
   return array(
        array(
              '#method' => 'example.echo',
              '#callback' => 'ExampleModule_echo',
              '#args' => array(
                    '#name' => 'text',
                    '#type' => 'string',
                    '#optional' => TRUE,
                    '#description' => t('Enter the string that the server should return.')
              ),
              '#return' => 'string',
              '#help' => t('Echos the provided string back to the consumer.')
          ),
   );
}

function ExampleModule_echo($text = "") {
   global $user;

   if($text == "")
      return services_error(t('There was no text provided to echo'), 404);

   return sprintf("%s just wanted to say: %s", $user->name, $text);
}

Comments

alouest's picture

interesting post.

But now, how do you call this service ?

Do you have some sample in js, java, python or other ?

Which server goes on for this service by default, xmlrcp, json, rest ?

thanks for your answer.

Déja Augustine's picture
Member since:
4 June 2010
Last activity:
49 weeks 1 day

Services are consumed (i.e. the functions of the service are used) by sending a data packet of the appropriate format to the protocol service address.

That is a long-winded way of saying that it really depends on both what platform you want to consume the web service on, which language you're working with, and what libraries are available.

If, for example, you are working in Python, you could use the JSON-RPC service provider module in Drupal and the following Python library: http://json-rpc.org/wiki/python-json-rpc

Or you could call the same JSON-RPC service from a linux terminal using cURL like such (note that this should be on a single line):


curl -i -X POST -d '{"jsonrpc": "2.0", "method": "example.echo", "params": { "text": "Echo Me This"}, "id": 1}' http://www.example.com/services/json-rpc

Note that the address being called is for the particular protocol we're wanting to interacting with (in this case JSON-RPC), and the above curl command is POSTing the JSON-RPC formatted message (which uses "method":"example.echo" to define which function to call, for example)

The above would return something to the effect of the following JSON-RPC response:


{
"id" : 1,
"jsonrpc" : "2.0",
"result" : "Anonymous just wanted to say: Echo Me This"
}

Now the exact same thing could be accomplished using xml-rpc, or any of the other service provider protocols. The protocol determines the format that the data is sent to and received from the service, but as I stated in the article, the Services Drupal module abstracts that out so that you can use any protocol to consume the service without having to make any changes to your Drupal service code.

If you look at http://drupal.org/node/113697 there are a number of examples there in a variety of protocols/languages. Also, if there is a specific programming language or platform you're using (.NET for example), there may be libraries that make it easy to access web services via a particular protocol (xml-rpc, for example). I would explore the possibilities by searching google for various platform/protocol combinations to see what is available for your project(s).

alouest's picture

Thank you for this great answer, very helpfull post.
I 've spent some times to do it, but i didn't manage to succed without calling a login (or auth key) method BEFORE calling the other one (echo for exemple).

Anonymous's picture

Very nice post, thanks a lot..

Buffa's picture

I'm a complete newbie in Drupal, PHP and Services, so I'm sorry if I'm mistaken in the below comment.

I believe that there is a missing array() around the single parameter shown in the example. As is, the example is not understood well by Drupal, and it believes there are 4 parameters, each with an incomplete specification.
By looking at other Drupal services (e.g. file_service.module) I noticed that the set of arguments is an array(), but also each method argument is an array of its own.
Fixing the code per the above (just adding another array() surrounding the existing one) made the function work.

I still want to thank the author for providing this article and example - it provided me with the push I needed to start writing my own service that I need for my project.

Lia's picture

Buenas Agustín , necesito hacerle unas preguntas sobre el módulo Service de Drupal, por favor usted podría responder a mi correo, saludos y mil gracias