Easy Sinatra

After attending the CodeRage challenge, I realised I didn’t know how REST API’s and JSON really worked. I knew what they were and their place in the stack but not how they really worked. The only way to figure that out is to build something that made use of these technologies.

We would be using Sinatra as the framework, Rspec for testing, and json and jsonify to parse JSON. Let’s start by creating the necessary files.

/src/sapify
└── .gitignore
└── Gemfile
└── README.md
└── Rakefile
└── api_server.rb
└── api_server_test.rb

Commit 1 (Link)

In .gitignore we can ignore files that shouldn’t be in version control. I usually have *.swp as I use vim. We can start building our Gemfile with the required gems.

# Gemfile
source :rubygems

gem 'rspec'
gem 'rack-test'
gem 'sinatra'
gem 'jsonify'

Rspec and rack-test are for testing, Sinatra and jsonify are for our application. Running bundle would install and check that we have the necessary gems that we’re gonna use.

Building our API server and our test suite.

#api_server_test.rb
require './api_server'
require 'rspec'
require 'rack/test'

set :environment, :test

describe 'The REST API server' do
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  it "returns all users" do
    get '/api/users/'
    last_response.should be_ok
  end  
end

We describe our test, followed by including Rack::Test::Methods so we can use methods such as get, post, put and delete in our tests. It also provides a nice object last_response which we can use to check the response.

With our test suite in place, we add a test method to our Rakefile as we would want to be able to run the tests by issuing just a rake test command.

Commit 2 (Link)

We are now ready to start building our application. We know we are going to have users. We start adding tests to check individual user id’s. The tests check that the response is what we expect.

Commit 3 (Link)

In this commit, we create a schema, a Person class where we have 2 attributes that can be accessed, id and name. Follwed by adding 2 people to our list, John and Peter.

Next, we proceed to write the methods for our API. The first method is to return all id’s of existing users when a get requests hits our /api/users/ endpoint. With this method complete, we should pass our first test.

The second method is to return the values of the user when a get request hits our /api/users/:id endpoint, where :id is a valid user id. It should return a json error if the user id does not exist. Now, when we run our tests, they should both pass.

Commit 4 (Link)

In this commit. I realise that I could make use of JSON.parse to parse the response. Previously, I comparing strings to make sure they match. Now with JSON.parse, we can compare the values in the hashmap’s instead.

We also add some new tests for creating a user. We check that the our api can create a new user but returns an error when the user already exists. We also check that the request is properly formed.

Commit 5 (Link)

Now that we’ve got the tests, we neeed to write the code pass the tests. We create a post request endpoint and parse the request. If there are missing elements, we return an error. If the JSON request is well formed, we check if the user already exists. Once we know that the user does not exist, we add the user to our list of users. If the user already exists, we return the corresponding error.

Commit 6 (Link)

Our API server now has the ability to query the users, and add new user. Now we would like to modify these users. We start out by writing the appropriate tests. First, we check that the user data is correctly modified on the request.

Next, we check that the server does not modify any data if the request is not well formed. Following that, we check that the server rejects the request if the user does not exist.

Commit 7 (Link)

Now that we have our tests, we can write the code for modifying the user details. We write a put request endpoint and parse the JSON request. Once again, we check if request is well formed, and rejects the request if it isn’t. Next we check if the user exists in our list. Only if the user exists, we modify the details. If not, we respond with the appropriate error message.

Further enhancements

We can further complete this API endpoint by writing a delete request endpoint to delete a user.

Conclusion

After this quick exercise, I learnt how REST APIs work. Writing these API endpoints were easy with Sinatra. I also learnt why JSON is used as a messaging format and parsing them in Ruby is trivial. On top of that, I picked up some Rspec knowledge!

Stanley Tan
@stnly