NUTS AND BOLTS REST Hands-On Lead image: Lead Image © Michal Bednarek, 123RF.com
Lead Image © Michal Bednarek, 123RF.com
 

RESTful APIs in practice

The Pure Doctrine

Internet providers often market their open interfaces as RESTful, but is this true of the APIs, especially when no standard exists? We introduce some REST providers and look at how closely their API architectures reflect the REST principle. By Marcus Nasarek

REST (representational state transfer) is a design principle applied to programming interfaces. Not so much a specific standard, it is rather a collection of guidelines for the design of web interfaces. REST is committed to the consistent use of hypermedia to scale a web service to the max and make it as flexible as possible. In the previous issue, I devoted an article to the theoretical superstructure of REST [1].

Once you have learned the principle, it is easy to familiarize yourself with new REST interfaces. Providers benefit from REST when they propagate open interfaces as soon as possible, thus making them attractive to many developers.

However, a RESTful API requires discipline in its design, which causes some providers to take shortcuts through proprietary formats and function calls. Integrating standards takes time, and not every developer or every company is enthralled with the idea of having to supplement documentation or add abstraction steps, especially as applications become more complex, requiring developers to put more effort into representing each relevant application state via unique URLs.

On Your Marks!

To make sure I compare like with like, I briefly introduce the central concepts of a RESTful API. The crux of a RESTful service is the resource, which can be a file, a database record, a certain application state, or an intermediate step in the program sequence. Rather than passing this resource directly to the interface, the developer accesses a generalized representation of it.

This indirect route via a representation is another important element of REST. The Rails framework and object-oriented programming (OOP) embody this principle. They also collect an additional presentation layer that neatly separates a user perspective and the application logic. From a database entry, Rails makes a model (or an object-oriented program an object) that is aligned more toward application logic and less on the database schema.

Hypermedia combines hypertext and multimedia. REST uses hyperlinks to connect documents, which allows navigation along a planned sequence. Hypermedia therefore allows you to combine resources in such a way that the next step is a result of the information available, thus defining the fourth criterion of a RESTful API.

Get Set!

Because uniform standards for REST do not exist, I reference the design criteria presented in my previous article [1]:

A RESTful API that deserves the name needs to implement these four criteria, although they do not guarantee quality in their own right. Even proprietary remote procedure call (RPC) interfaces over HTTP scale well and are easy to use, but they should not be called RESTful if they do not incorporate REST.

Go!

From the list of providers who call their APIs "RESTful," I chose the following three to examine in this article. Two are popular Internet services from Twitter [2] and WordPress [3]. Because of the high number of users and open interfaces, you can expect an affinity with REST. A smaller provider, figo [4], seeks to emulate the online banking interfaces used by banks with REST.

In this article, I introduce the APIs of these services and assess their "RESTfulness," according to the above criteria, with a score ranging from 1 to 3 (Table 1). The rankings indicate how the provider implements the RESTful API.

Tabelle 1: Comparison of REST Interfaces

Implementation Score*

REST Criterion

Twitter

WordPress

figo

URIs for Resources

3

2

3

Presentation Layer

3

3

3

Hypermedia Data Formats

2

2

1

Self-Explanatory Process

3

2

3

Total

11

9

10

* 1, almost; 2, on average; 3, comprehensively.

Twitter

Twitter is a web service for short messages of 140 characters in length. However, they can now be supplemented by photos, videos, location information, and surveys, which significantly expands the functionality of the corresponding interface.

Twitter offers several APIs, well documented in the Developer Portal [5], that allow you to read and write Twitter data, compose new tweets, or read user profiles and follower data. The OAuth protocol [6] authenticates the API client, and JSON is the data format. A separate streaming API is available for real-time monitoring of tweets.

If you want to use the Twitter API, you need to register your account and your application with the Twitter platform [7] before generating the access token. To register an application, Twitter just asks for some descriptive data and that you consent to the terms of use. Figure 1 shows the configuration site for the application. Later, the API client uses the access token that is generated during logon to be identified by OAuth.

Once you have registered to use the Twitter API, you are taken to this configuration page.
Figure 1: Once you have registered to use the Twitter API, you are taken to this configuration page.

The numerous libraries [8] help you develop a client and make it significantly easier to program API clients. For example, Listing 1 shows a Ruby client based on the twitter Ruby library that uses an access token to authenticate with the 23Cent user account. The client then loads and displays the user's name, ID, unique URI, creation date, and any available tweets for the account. The value for the access key and token can be found in the application configuration below Keys And Access Tokens.

Listing 1: 23centify.rb

01 require 'twitter'
02
03 client = Twitter::REST::Client.new do |config|
04   config.consumer_key        = "<your_consumer_key>"
05   config.consumer_secret     = "<your_consumer_secret>"
06   config.access_token        = "<your_access_token>"
07   config.access_token_secret = "<your_access_secret>"
08 end
09
10 user = client.user("23Cent")
11 puts user.name
12
13 def collect_with_max_id(collection=[], max_id=nil, &block)
14   response = yield(max_id)
15   collection += response
16   response.empty? ? collection.flatten : collect_with_max_id(collection, response.last.id - 1, &block)
17 end
18
19 def client.get_all_tweets(user)
20   collect_with_max_id do |max_id|
21     options = {count: 200, include_rts: true}
22     options[:max_id] = max_id unless max_id.nil?
23     user_timeline(user, options)
24   end
25 end
26
27 tweets = client.get_all_tweets("23Cent")
28
29 tweets.each do |t|
30   puts t.id, t.uri, t.created_at
31   puts t.full_text
32 end

In this example, however, a library hides the API calls by superimposing its own logic. It can hardly be assessed whether the API was originally RESTful. Help comes in the form of the twurl [9] Ruby script, which imitates the Curl web crawler and operates almost directly with the API calls. The documentation [5] reveals that the API supports the HTTP GET, POST, and DELETE methods.

The GET method is the best choice for completing the first task of the program in Listing 1 and displaying the name of the user (user; via show). The documentation suggests the resource URL https://api.twitter.com/1.1/users/show.json. If corresponding authentication is required with OAuth, a GET provides detailed information about the user on this URL with the appropriate Twitter alias (screen_name, here 23Cent):

GET https://api.twitter.com/1.1/users/show.json?screen_name=23Cent

Listing 2 shows a small excerpt from the response in JSON format.

Listing 2: show.json?screen_name=23Cent

01 [...]
02 {
03         "id":16590222,
04         "id_str":"16590222",
05         "name": "Marcus Nasarek",
06         "screen_name":"23Cent",
07         "location":"Berlin, Germany
08         "url":"http://marcus-nasarek.de"
09         <I>[...]<I>
10 }
11 [...]

The Twitter API is quite intuitive. Developers largely address the resources and application states with unique URIs, and the responses to method calls (GET, POST, and DELETE ) provide sufficient information for the next application steps.

Twitter provides numerous URLs in the JSON responses that allow it to address additional resources. JSON itself [10] is not a hypermedia format; the developers extend it through various JSON schemes. Twitter uses its own schema, which is also multimedia-capable. From the perspective of a RESTful API, this certainly is a pragmatic and comprehensible method, but it's not completely "RESTful."

The fact that Twitter reuses and contextually combines data suggests that the service neatly separates the presentation layer. All in all, Twitter's REST API is quite close to the ideal of a RESTful API.

WordPress

WordPress is an open source application for blogs. You can install it on your own web server or use it as a pre-configured web service [3]. In addition to various other APIs, a well-documented REST API [11] is also available. It also uses JSON; the hypermedia capability is based on the hypertext application language (HAL) standard [12].

In this section, I look at the WordPress.com web service API, which has been upgraded significantly. Clients authenticate by using OAuth2. Developers can pick up a corresponding access token by registering their client applications [13].

WordPress.com also provides a practical sandbox, the WordPress.com Console, which allows developers to familiarize themselves with the API (Figure 2). WordPress.com also offers comprehensive documentation of the REST API.

WordPress.com supplies a sandbox for your first experiments with the REST API.
Figure 2: WordPress.com supplies a sandbox for your first experiments with the REST API.

When it comes to using the sandbox, it can be noted that deleting a resource does not rely on the HTTP DELETE method; instead, WordPress uses a POST call, because WordPress wants to run on as many servers as possible but does not want to support all possible HTTP methods. The provider is probably playing it safe and only offering the most commonly used methods.

The user queries all the blog posts of a person in this example; Listing 3 partially shows the returned API response in JSON format. The response contains very detailed information with numerous references to additional resources, with the appropriate URIs, where necessary. Thanks to the detailed metadata, further steps become intuitively possible – through appropriate references to the permissions – as well as the IDs required for calling other resources.

Listing 3: Response to WordPress Request

01 [...]
02 {
03   "sites": [
04     {
05       "ID": 20267300,
06       "name": "Raspberry Pi Lab",
07       "description": "Code AMBER",
08       "URL": "http://raspilab.blog",
09       "user_can_manage": false,
10 [...]
11 }
12 [...]

The Wordpress.com API thus facilitates intuitive use. To a great extent, it is also possible to address resources and application states with unique URIs. This is not true of each step of the application. The responses to queries and the data transmitted are very detailed and provide many tips for the next steps. However, a developer traverses a certain learning curve to grasp the WordPress-specific processes and their logic.

Using the URIs and embedded multimedia formats, WordPress converts the JSON format to a hypermedia format, although it does not rely on standards to do so. The structure of the data is only revealed after a careful study of the documentation. Transmitting a new post thus requires a fairly complex data construct, with a POST against /sites/$site/posts/new.

At this point, the REST API packages various formats and resources in a JSON format and attempts a balancing act between as few resource URLs and as much functionality as possible. However, doing so appears to be a break with the pure doctrine.

figo

Figo [4], which offers web services for banks, comes into play as a representative of the German-speaking countries. Other services can access the accounts of thousands of banks through the provider's REST API – with the authorization of the account holder, of course. Recent changes in the law have created a legal framework for providing such services across Europe.

From the perspective of a REST API, and with a view to the traditional banking interfaces, linking these worlds appears to be a challenge. Most banks in Germany support an open interface called FinTS [14], whose intellectual origins reach back to BTX banking (announced at CeBIT in 1983). The uncharted territory of that time now seems somewhat outdated: FinTS uses XML (i.e., a hypermedia format), but it simply repackages the classic messages.

The figo API implements the FinTS format or, alternatively, the online banking web page of a bank in REST logic. If you want access, you need to register by email for access to the API and test environment. In response, you receive a client ID, an OAuth secret, and the access credentials for the demo account.

The API documentation and the teaching tool for developers are pleasingly extensive [15]. In addition to a demo account, the SDKs for the major scripting languages and corresponding sample programs are available on GitHub [16], which figo also documents amply. The figo API adheres strictly to API conventions, so calls are intuitive after examining the API. The API specification itself is available in text format and as an OpenAPI-2.0-compliant JSON file, which means you can load the specification into Swagger IDEs.

Listing 4 shows a small Ruby script that accesses the demo account of the figo server. Listing 5 displays the output and shows how the developers retrieve account information with a few lines of code. The demo account functions as an aggregation service, and the account data ends up on figo's servers. A service can theoretically gather account information from several banks and make it centrally available. Alternatively, an account query takes place immediately against the respective bank account. For this, the user must re-enter the access information for the account. SDKs are available for both variants.

Listing 4: Demo Account Query with Ruby

01 require "figo"
02
03 session = Figo::Session.new("ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ")
04
05 # output of the account number and balance
06 session.accounts.each do |account|
07   print "ID: #{account.account_id}"
08   print " | ANR: #{account.account_number}"
09   puts " | BALANCE: EUR #{account.balance.balance}"
10 end
11
12 # Output of all transactions and some transaction details
13 puts "The last 10 sales for account ID A1.1"
14 session.get_account("A1.1").transactions[-11..-1].each do |transaction|
15   print "#{transaction.name}"
16   d = Date.strptime("#{transaction.value_date}", '%FT%T%:z')
17   print " | #{d.strftime("%a, %d.%b %Y")}"
18   print " | #{transaction.amount}"
19   puts " | #{transaction.purpose[0..20]}"
20 end

Listing 5: figo Demo Account Response

01 $ ruby figo_demo.rb
02 ID: A1.1 | ANR: 4711951500 | BALANCE: EUR 3250.31
03 ID: A1.2 | ANR: 4711951501 | BALANCE: EUR 2190.42
04 ID: A1.4 | ANR: 4711951502 | BALANCE: EUR 11754.65
05 The last 10 transactions for account ID A1.1"
06 Kanne Brottrunk Gmbh & Co. Kg | Tue, 06.Aug 2013 | -50.1 | Invoice no. 98421312
07 Blumen Meier | Wed, 07.Aug 2013 | -18.2 | Inv. no. 2013/312 100
08 Schnitzelei | Thu, 08.Aug 2013 | -230.0 | Ec 8233123 0707123388
09 Bitstream, Inc. Myfonts | Fri, 09.Aug 2013 | -29.9 | Usa Marlborough Usd 2
10 Berlin Transport Authority | Sat, 10.Aug 2013 | -57.92 | account with us 0001423
11 Techniker Krankenkasse | Sun, 11.Aug 2013 | -230.0 | Insurance no. 123197
12 Dr. House Solutions Gmbh | Mon, 12.Aug 2013 | -1753.2 | rent July 2012
13 Airfreu Ag | Tue, 13.Aug 2013 | 2300.0 | salary
14 Christoffel Mission to the Blind | Wed, 14.Aug 2013 | -30.0 | Elv14ß0890843 06.07 1
15 Ga No00021288 Sort Code20020000 2 | Thu, 15.Aug 2013 | -100.0 | 18.07/09.57hours Rathau
16 Your flower delivery | Fri, 16.Aug 2013 | -24.99 | Inv. 2312, 2/12/2013

The figo API addresses all resources through unique URIs and is perfectly documented. However, how intuitive the use of the API is, based on the available information, depends on how well the developer knows the underlying transactions. Flowcharts in the documentation help developers understand the process of a classical customer bank dialog.

Figo uses its own JSON specification as a data exchange format. Apparently, the format has little to do with hypermedia. However, the API talks to a truth table on the bank's website, which is often hosted on mainframes. Access to data or transactions is therefore an "atomic business transaction," as it is known in FinTS-speak.

The flow charts indicate that some of the individual steps are related and that specific additional resources are linked to them. It would make sense to integrate URIs and labels in the API response. They could refer to the logical next steps or relevant resources, unlike the value tables in text form. Labels like image or media, as provided for HTML5, generally help developers organize machine-readable parameters in the responses.

With regard to the bank interfaces, the figo API uses its own presentation layer in each case to simplify the user's view. With this in mind, this API comes close to the ideal state of a RESTful API.

Conclusions

The three APIs featured in this article stick firmly to the REST principle. At the same time, by choosing JSON, none of the three use a genuine hypermedia format but take a pragmatic approach with their own JSON specifications. Formally, they fail the REST check.

The operating principle of the hypermedia format can be imitated in JSON; Twitter does a better job of this than WordPress.com, whereas figo keeps too much to simple tables of values to use hypermedia options consistently. The processes shown in the documentation with their branches can be mapped well in a hypermedia format; the API does not fully grasp the opportunities here.

All three manage without difficulty to abstract the data, creating an application layer and a user-oriented presentation layer, which is in the interest of providers, because it has a positive effect on version maintenance. The user aspect is particularly evident with Twitter and figo.

In terms of the process, figo is a bit more difficult because it depends on payment transaction mechanisms and the customer-bank dialog, but the problem is solved within the framework of its possibilities.

For all three of the APIs presented here, you can see that the implementation of RESTful principles are understood for three of the four criteria. Against the background of advancing developments in the field of artificial intelligence, it would certainly be preferable to include hypermedia more consistently, because that means an API can intuitively navigate along a desired process from one resource to another. At the same time, it clearly marks multimedia resources as such. This facilitates the machine readability of the code and therefore the ability to automate integration.