Project Ramon

A learning journey from a Ruby noob perspective

Learning to design an API in Ruby

api2_header_img

Hello and happy Friday!

Continuing with my learning on the topic of APIs in Ruby, I’ve been reading a book entitled Rails 3 in Action by Yehuda Katz and Ryan Bigg. If you’re using Rails 4, they also have a new release under the same title which also features Steve Klabnik as an author, and is slated to be available at the end of March according to Amazon.

Today, I’m going to cover what I’ve read so far from chapter 13 “Designing an API”.

What is an API

An application programming interface makes allowances and also specifies how some software components should interact with one another.

An API is usually related to a software library (also referred to as a client), but can also be an implementation of a protocol or used in the context of web development typically defined as a set of HTTP requests and a definition of the structure of response messages.

The two main formats of a web API are XML (Extensible Markup Lanugage) and JSON (Javascript Object Notation). Web APIs used to be synonymous of communication between two devices over the web, but recently have been moving away from Service-Oriented Architecture (SOA) towards the more direct Representational State Transfer (REST) resources and Resource-Oriented Architecture (ROA). Web APIs allow the combination of multiple APIs into apps known as mashups.

API Setup

Step 1: TDD

We use the rack-test gem to test what our API’s URLs are doing. To set this up, we can add structure to our spec folder. We can add an apis folder (keeping it plural in the event that our application will use more than one api in the future), and inside of this apis folder we can add a v1 folder to house the tests for version 1 of our API.

Inside of the v1 folder we can create a spec file named after our api that will have the similar structure of api_name_spec.rb, and place the following inside of the file.

We’re passing in an option of type: :api into our describe block in order to have the Rack::Test::Methods module loaded into each test automatically, instead of having to manually enter this into all our test files.

Next we need to create another sub-folder inside of our spec folder. When we’re done, our subfolder tree should look like spec/support/api/helper.rb, and contain the following code:

And in our spec_helper.rb file inside of the Rspec.configure block we can add the following:

Now that we’ve defined our ApiHelper module in our /spec/support/api/helper.rb file, we can consider this included with any test marked with an API and with the :type option passed in as we saw earlier.

And we’ve defined the app method so that Rack::Test::Methods knows which application to perform in.

Step 2: Create a Spec
In our file spec/apis/v1/your_api_name_spec.rb we can create a user to use with our request to the API. Lets follow the book and good TDD practice by using let to set up a user 1 time to be used in more than one place.

We also need to setup Devise to include the token_authenticatable module so we can authenticate API requests from users by a token they will provide with each request. The authentication token is an identifier that will allow us to know what API features to present to our users.

We can head on over to the User model and change our Devise attributes to the following:

Follow this up with a migration adding the string authentication_token to our users table so we can actually store this token identifier. Once we’ve added this database field, we can run rake db:migrate to create the column in our database, and rake db:test:prepare to migrate our test environment’s database.

We need to add a callback to our User model so that authentication tokens are generated when a user is created, or for when users are updated but don’t have a token. We can do this with a before filter like so:

With this callback to create a User token all setup, let us finish out the spec.

The get method on line: 16 makes a GET request with the provided URL, and is provided by the module Rack::Test::Methods. Since we want to keep our code dry we’re placing the URL with let so we don’t have to re-define it several times in future tests.

Step 3: Define routes to the API
I found this blog post from Collective Idea, an agile software-team that does Rails (among other things) located in Holland MI. The post is very informative about the how and why of name spacing an API, versioning, creating a subdomain to help us load balance traffic at the DNS level and using respond_to to handle JSON requests.

Here’s a quote from the post:

API versioning can be controversial1, but if you want to add version info into your URLs, simply add another namespace to your routes:
namespace :api do
namespace :v1 do
resources :people
end
end
Now your version 1 API is at URLs starting with /api/v1/ and your controller would be in a v1 folder with the added v1 module.
class API::V1::PeopleController < ApplicationController

end
You could now support multiple versions at the same time, just make sure you have a very good test suite so you don’t break v1 when you go to v2.

So assuming that we want to version our api we can define our routes like so:

This gives us pretty URL names and helps keep our now name spaced API clean and well organized.

This is pretty much what I’ve been reading and learning to comprehend over the past day or so. I’m looking forward to next week, as I’ll have an AirPair with Adam Cuppy on using a 3rd party API in my current project.

Stay tuned, and have a great weekend!

Advertisements

Categories: Ruby on rails

Tags:

1 reply

Trackbacks

  1. API Design in Ruby (pt. 2) | Project Ramon

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s