Contents
Understand Representational State Transfer (REST) in Ruby
Build a simple RESTful client with Ruby
Representational State Transfer (REST) is an architectural style for web-based communication that permits clients to talk to servers in a unique fashion. In particular, REST represents resources within a given server as uniform resource identifiers (URIs), simplifying the implementation of REST architectures on the Hypertext Transport Protocol (HTTP). Let's begin with an introduction to the ideas behind REST and HTTP. We'll then explore data representation, and then implement a simple REST client in the Ruby language.
Quick introduction to HTTP
Let's start with a quick introduction to HTTP, as it's important to understand for individual REST transactions. Although HTTP is the foundational communication protocol that connects web browsers to servers, HTTP is a useful protocol for transferring many types of data other than HTML.
HTTP is a request-and-response protocol—that is, clients make requests, and servers satisfy those requests with a response. The actual protocol for HTTP is quite readable to humans, and to demonstrate HTTP, I use telnet to make a request to a web server.
Listing 1 provides an HTTP request and a partial
response from a web server. I initiate the request with telnet by
specifying the web server domain name and port (80 is a typical HTTP
port). Telnet responds first with a Domain Name System resolution of the
domain name to an IP address, and then indicates that I'm connected to the
web server. I then specify a request line (which contains my HTTP
GET
method, a path to the resource on the web
server, and the protocol I'm using—in this case, HTTP
version 1.1). Next, I provide a set of request headers (which can be quite
large, but because I'm typing it out, I simply specify the
Host
request header, which indicates the host
and optional port of which I'm making the request). My request is followed
by a blank line, which tells the web server that my request is complete.
The web server then provides a response, indicating the protocol used and
providing a status code (in this case, 200 OK
,
indicating a good request) and additional response headers. A blank line
then appears, followed by 1944 characters of the response. This is a
representation of the resource—in this case, an HTML
document.
Listing 1. Performing an HTTP transaction with telnet
$ telnet mtjones.com 80 Trying 198.145.43.103... Connected to mtjones.com. Escape character is '^]'. GET /index.html HTTP/1.1Host: example.org HTTP/1.1 200 OK Date: Sun, 25 Mar 2012 05:33:07 GMT Server: Apache Last-Modified: Sat, 26 Sep 2009 20:22:36 GMT ETag: "2c984bf-798-d3451b00" Accept-Ranges: bytes Content-Length: 1944 Vary: Accept-Encoding Content-Type: text/html <DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ...
This example illustrates a simple transaction, but several methods are
actually implemented in HTTP. The GET
method is
used to retrieve a resource from the web server, where
HEAD
is used to get only meta-information about
the resource (not the actual content). A POST
method is used to provide new content to the web server, and the
PUT
method is used to place data for an
existing resource on the web server.
Table 1 provides a full list of HTTP methods.
Table 1. Common HTTP 1.1 request methods
Method | Idempotent | Description |
---|---|---|
OPTIONS | Yes | Request for information about communication options |
GET | Yes | Retrieves representation for a URI |
HEAD | Yes | Retrieves meta-information for a URI |
PUT | Yes | Creates or replaces a resource with a representation |
POST | No | Augments an existing resource with a representation |
DELETE | Yes | Deletes a resource that the URI specifies |
What's important to note from this short exploration of HTTP is that it's a protocol that supports basic operations on resources. Although HTTP is commonly used today to transfer web content between servers and clients, it's also a growing protocol for distributed system interactions and for the development of application programming interfaces (APIs) that allow heterogeneous systems to communicate and share data.
Let's now climb up the protocol stack to explore the REST layer.
What is REST?
REST is more an architectural style and less a specific design or implementation. A RESTful architecture is defined by a simple set of constraints, illustrated in Figure 1. At the core of a RESTful architecture are a set of resources. These resources are identified by URIs (such as a Uniform Resource Locator [URL]) and an internal representation (commonly a form of self-describing data, which you'll explore shortly). Finally, there is a set of operations through which you can manipulate the resources.
Figure 1. High-level view of a RESTful architecture

In more concrete terms, these resources can represent data objects using of
a variety of types (such as JavaScript Object Notation [JSON]). You can address the
resources through URLs (such as
http://www.mtjones.com
) using a set of standard
operations (HTTP GET
,
POST
, DELETE
, and
the like). Using HTTP as a transport simplifies the development of RESTful
architectures tremendously, because they use a well-known and stable base
protocol. HTTP also is widely available and requires no new configuration
to use, including Internet services such as gateways, proxies, security
enforcement entities, and HTTP caching services. To make REST servers highly
scalable, you can exploit other useful features, such as load balancing.
Characteristics of a RESTful architecture
Although RESTful architectures have considerable freedom in their implementation, one set of characteristics is important.
REST defines a client-server architecture in which clients have access to server resources through a representation that the server exports. Clients don't access the resources directly, but instead a representation of the resource through a uniform interface. As with many client-server architectures, REST is implemented as a layered architecture, allowing it to exploit the various capabilities that the lower layers (HTTP load balancing, and so on) provide.
But a key aspect of RESTful architectures is that they're stateless. The server cannot maintain any client context between transactions, and every transaction must contain all information necessary to satisfy the particular request. This characteristic tends to make RESTful architectures more reliable and also helps to expand their scalability.
Sample REST interface
Look at a sample REST implementation to illustrate some of the characteristics of a RESTful architecture. Recall that REST relies on client-server interactions (see Figure 2). A client application makes a request that is translated into a RESTful HTTP request. This request is initiated like any other HTTP transaction from a client to a server. The server processes the request and responds appropriately.
Figure 2. Layered architecture of RESTful interactions

An interesting example of a REST API that you can use to build a simple client is CrunchBase. CrunchBase is a free database of technology-related companies, people, and investors. In addition to providing a traditional web front end, CrunchBase offers a REST/JSON-based interface over HTTP.
CrunchBase implements three actions through its API:
- show (to retrieve information on a specific entity)
- search (to retrieve a list of entities that match a given search criterion))
- list (to retrieve all entities within a given namespace)
CrunchBase also exports five namespaces for its
data—see Figure 3—along
with the URL form used for CrunchBase REST interactions.
(The namespaces are company, person, product, financial-organization, and service-provider.)
Note that the /v/1
indicates the
version of the API, which is currently 1. Also note the
permalink field, which indicates the entities' unique
name within their database.
Figure 3. Namespaces in the CrunchBase API

If you wanted to get current information about IBM, you could construct a URL using the company namespace (try this in your browser):
http://api.crunchbase.com/v/1/company/ibm.js
You can type this URL into a browser, and the browser will render the textual (JSON-based) response for you (while consuming the HTTP headers). Look at this in more detail as you explore CrunchBase's data representation in the JSON format.
Introduction to self-describing data
Communicating among heterogeneous systems introduces some interesting problems, one of which is the serialization of data for transfer. Machines represent data in different ways (from differing floating-point representations to the standard byte-ordering conflict). Early implementations included the Abstract Syntax Notation (ASN.1) format and the External Data Representation (XDR) protocol (used within Network File System). Other approaches include XML, which encodes data within ASCII-formatted documents.
In the past six years, the JSON format has grown in popularity. As the name implies, JSON was derived from the JavaScript language and used to represent self-describing data structures such as associative arrays. Despite its name, JSON is a common data interchange format and is supported by a variety of languages. It's also trivial to read.
Now look at an example of JSON, in particular—one
through the CrunchBase REST interface. This example uses the interactive
Ruby shell (irb
), which allows you to
experiment with Ruby in real time.
As shown in Listing 2, you begin by executing the
interactive Ruby shell. You prepare your environment by loading a few
modules (in particular, the JSON and HTTP components) and define your URI.
Note here that the URI is a full CrunchBase request (in the company
namespace, with a permalink of ibm
). You
provide this to the get_response
method of
Net::HTTP
, which is a shortcut for performing a
GET
request on the specified URI (parsed to its
individual components through the URI.parse
method). If you emit the resp.body
, you can see
the JSON data that was returned. This includes a set of name-value pairs
(such as "name" and "IBM"). You use the
JSON.parse
method to parse the response into a
Ruby object structure. Finally, you extract a particular value by
specifying its name.
Listing 2. Interacting with CrunchBase using Ruby
$ irb irb(main):001:0> require 'rubygems' => true irb(main):002:0> require 'json' => true irb(main):003:0> require 'net/http' => true irb(main):004:0> uri = "http://api.crunchbase.com/v/1/company/ibm.js" => "http://api.crunchbase.com/v/1/company/ibm.js" irb(main):005:0> resp = Net::HTTP.get_response(URI.parse(uri)) => #<Net::HTTPOK 200 OK readbody=true> irb(main):006:0> puts resp.body {"name": "IBM", "permalink": "ibm", "crunchbase_url": "http://www.crunchbase.com/company/ibm", "homepage_url": "http://www.ibm.com", "blog_url": "", "blog_feed_url": "", "twitter_username": "", "category_code": "software", "number_of_employees": 388000, ... => nil irb(main):007:0> parsedresp = JSON.parse(resp.body) => {"updated_at"=>"Wed Feb 01 03:10:14 UTC 2012", "alias_list"=>nil, ... irb(main):008:0> irb(main):009:0* puts parsedresp['founded_year'] 1896 => nil irb(main):010:0>
From Listing 2, you can see how easy it was to prototype a quick data extraction from a JSON response (seven lines). Take this one step farther now and build a simple, reusable API for interacting with CrunchBase.
Building a simple REST client
Before you build your REST client, you must install a few things. If you don't have Ruby installed, install this. Because I use Ubuntu, I use the Advanced Packaging Tool for most of these installation requirements (and the Ruby gem package manager for another).
Grab the Ruby package with:
$ sudo apt-get install ruby
Optionally, grab the Interactive Ruby Shell
(irb
), which is a useful way to experiment with
the Ruby language:
$ sudo apt-get install irb
Finally, you need the JSON gem for Ruby. The following code shows how to get both the gem front end and the JSON gem.
$ sudo apt-get install rubygems1.8 $ sudo apt-get install ruby-dev $ sudo gem install json
With your environment ready, start to build the simple REST client API with Ruby. You saw in Listing 1 how to communicate with an HTTP server in Ruby and how to parse a simple name from a JSON object. Build on this knowledge with a set of Ruby classes that implement a simple API.
First, look at the two sample classes that interact with the CrunchBase REST server. The first focuses on the company namespace; the second focuses on the person namespace. Note that both extend a minimal set of methods but are easily extensible for other data elements.
Listing 3 provides the API to interact with the
company namespace. This class extends a constructor method
(initialize
) and four methods used to extract
data from a JSON-based company record. The theory of operation for the
class is that the user creates an instance of the object, providing a
company (permalink) name. As part of the constructor, CrunchBase is
solicited for the company record. You dynamically construct the URL,
adding the company name passed as part of the
new
method. The
get_response
method is used to retrieve the
response, and the parsed result (a hash object) is loaded into an instance
variable (@record
).
With the parsed record available, each method when called simply extracts
the desired data and returns it to the user. Methods
founded_year
,
num_employees
, and
company_type
are straightforward, given the
discussion from Listing 1. The
people
method requires slightly more
interpretation.
The JSON response from CrunchBase is represented by a hash containing some
number of keys. Note that in the first three methods, you specify the key
to return the value. The people
method iterates
through a hash identified by the key
relationships
. Each record contains an
is_past
key (which is used to identify if the
person is no longer with the particular company), a
title
key, and a person key, which contains
first_name
,
last_name
, and
permalink
. The iterator simply walks through
each, and when the key is_past
is false,
extracts the person and title and creates a new hash from this
information. This hash is returned when no further keys are found. Note
that you can view this entire hash within IRB by simply emitting the
JSON.parse
-ed response.
Listing 3. Ruby CrunchBase API for the company namespace (company.rb)
require 'rubygems' require 'json' require 'net/http' class Crunchbase_Company @record = nil def initialize( company ) base_url = "http://api.crunchbase.com" url = "#{base_url}/v/1/company/#{company}.js" resp = Net::HTTP.get_response(URI.parse(url)) @record = JSON.parse(resp.body) end def founded_year return @record['founded_year'] end def num_employees return @record['number_of_employees'] end def company_type return @record['category_code'] end def people employees = Hash.new relationships = @record['relationships'] if !relationships.nil? relationships.each do | person | if person['is_past'] == false then permalink = person['person']['permalink'] title = person['title'] employees[permalink] = title end end end return employees end end
Listing 4 provides a similar function to the
Company
class discussed in Listing 3. The
initialize
constructor operates in the same
way, and you have two simple methods for extracting the name of the given
person (from his or her permalink). The
companies
method iterates the hash for the
relationships
key and returns the firms
(companies) that this particular person has been associated with in the
past. The associated companies are returned in this case as a simple Ruby
array (of permalinks).
Listing 4. Ruby CrunchBase API for the person namespace (person.rb)
require 'rubygems' require 'json' require 'net/http' class Crunchbase_Person @record = nil def initialize( person ) base_url = "http://api.crunchbase.com" url = "#{base_url}/v/1/person/#{person}.js" resp = Net::HTTP.get_response(URI.parse(url)) @record = JSON.parse(resp.body) end def fname return @record['first_name'] end def lname return @record['last_name'] end def companies firms = Array.new @record['relationships'].each do | firm | firms << firm['firm']['permalink'] end return firms end end
Note that in these two simple classes Ruby hides the complexity of dealing
with the HTTP server in the Net::HTTP
class.
The complexity of parsing the JSON response is completely simplified
through the JSON gem. Let's now look at a couple of applications of these
APIs to illustrate their use.
Building some simple applications
Begin with a demonstration of the classes. In the first example (see Listing 5), you want to identify the people associated with a given company (based on the company's permalink). The record retrieved from the company namespace contains a list of permalinks for people associated with the company. You iterate that hash of people and retrieve a record of the individual based on that permalink. That record provides you with the first and last name of the individual, and his or her title is part of the company record (returned as part of the name-title hash).
Listing 5. Identifying people associated with a company (people.rb)
#!/usr/bin/ruby load "company.rb" load "person.rb" # Get argument (company permalink) input = ARGV[0] company = Crunchbase_Company.new(input) people = company.people # Iterate the people hash people.each do |name, title| # Get the person record person = Crunchbase_Person.new( name ) # Emit the name and title print "#{person.fname} #{person.lname} | #{title}\n" end people = nil company = nil
You execute the script in Listing 5 as shown in Listing 6. With the script, you provide a company permalink, and the result is the individual's first and last name and title.
Listing 6. Testing the people.rb script
$ ./people.rb emulex Jim McCluney | President and CEO Michael J. Rockenbach | Executive Vice President and CFO Jeff Benck | Executive Vice President & COO $
Now, look at a more complex example. This example takes a given company, and then identifies the executive staff. It then identifies the companies that those executives have worked for in the past. Listing 7 provides the script, called influence.rb. As shown, you accept a company name as the argument, retrieve the company record, and subsequently retrieve a hash of the people currently at that company (through the people method). You then iterate through those people and identify the chiefs through their title (not entirely accurate, given the variability of titles in CrunchBase). For any identified chiefs, you emit the companies that those individuals have worked for (by retrieving an array of companies from the person record).
Listing 7. Executive relationships and influence (influence.rb)
#!/usr/bin/ruby load "crunchbase.rb" input = ARGV[0] puts "Executive Relationships to " + input company = Crunchbase_Company.new(input) people = company.people # Iterate through everyone associated with this company people.each do |name, title| # Search for only certain titles if title.upcase.include?("CEO") or title.upcase.include?("COO") or title.upcase.include?("CFO") or title.upcase.include?("CHIEF") or title.upcase.include?("CTO") then person = Crunchbase_Person.new( name ) companies = person.companies companies.each do | firm | if input != firm puts " " + firm end end end end
Listing 8 illustrates this script for the big data company, Cloudera. As you can see from Listing 7, Ruby and its gems hide many of the details from you, allowing you to focus on the task at hand.
Listing 8. Testing the influence Ruby script
$ ./influence.rb cloudera Executive Relationships to cloudera accel-partners bittorrent mochimedia yume lookout scalextreme vivasmart yahoo facebook rock-health $
Using other REST HTTP methods
In the simple examples shown here, you only used the GET
method was used to extract data from the CrunchBase database. Other sites
may extend an interface to retrieve and send data to their particular REST
server. Listing 9 provides an introduction to other
Net::HTTP
methods.
Listing 9. HTTP methods for other RESTful interactions
http = Net::HTTP.new("site url") # Delete a resource transaction = Net::HTTP::Delete.new("resource") response = http.request(transaction) # Post a resource resp, data = Net::HTTP.post_form( url, post_arguments ) # Put a resource transaction = Net::HTTP::Put.new("resource") transaction.set_form_data( "form data..." ) response = http.request(transaction)
To simplify the development of REST clients even further, you can use
additional Ruby gems such as rest-client
(see
Related topics). This gem provides a REST layer
over HTTP, offering methods such as get
,
post
, and delete
.
Going forward
I hope that this quick introduction to REST principles with Ruby illustrates the power of the web-based architecture as well as why Ruby is one of my favorite languages. REST is one of the most popular API architectures for compute and storage clouds and is therefore worth the time to understand and explore. In Related topics, you'll find links to additional information on the technologies used in this article as well as other REST articles on developerWorks.
Downloadable resources
Related topics
- REST is a software architecture for communication between distributed systems. REST was first introduced by Roy Fielding in his doctoral dissertation, entitled Architectural Styles and the Design of Network-based Software Architectures, at the University of California, Irvine.
- REST is built on top of HTTP. Learn more details of HTTP in the Internet Engineering Task Force Request for Comments-2616 (for the 1.1 version).
- Get an interesting introduction to REST in the video, Intro to REST, provided by Joe Gregorio of Google.
- Learn more about several useful ideas and technologies explored in this article: URI, URL, and JSON.
- Ruby is by far my favorite scripting language and one of the most intuitive languages I've ever used. Ruby is great not only for quick prototyping and experimental development but also for production software development. If you're new to Ruby, you'll thank yourself later for taking this great introduction to Ruby, Ruby in Twenty Minutes.
- Read more about REST in these developerWorks articles:
- RESTful Web services: The basics (Alex Rodriguez, November 2008) provides a useful introduction to REST and its basic properties.
- REST application programming (Paul Sonnenberg, September 2010) focuses on REST-based Java™ development.
- Using the Twitter REST API (Brian M. Carey, June 2009) introduces you to Twitter's REST API.
- For an even simpler REST implementation in Ruby, try the
rest-client
gem at Github. This REST client simplifies most major operations and allows you to focus on your application. - Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.