RESTful APIs in practice

The Pure Doctrine

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.

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.

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.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus