Project 5 - Paradex. What is an API? Why is an API? How to an API?

Simple API Construction w/ Ruby 2.1.3 and Rails 4.2.3

Paradex Repo

I’ve been using some external APIs on a few projects for a while now, so I think it’s time for me to learn more about what an API is and how I can construct a simple API within my application. While I was thinking how I am going to proceed with this post, I was also doing a quick assignment on my philosophy course. I began to wonder, is there an API that list out most (if not all) of the paradoxes? I didn’t really look into it, because I am going to constuct one in this post anyways! In Paradex (short for Paradox Index, genius, I know), we’ll create a basic JSON API that returns a list of paradoxes and their respective authors/descriptions.

What is an API

To put it simply, an Application Programming Interface (API) is a set of routes from one software that can be accessed by developers anywhere to incorporate that software’s function in their own projects. With an API, developers can easily gain certain information that would otherwise be difficult to obtain. Previously in some of the projects, I used the Riot Game’s API to access certain information from the game League of Legends, as well as processed payments through the use of Stripe API. To learn more about API, check out this post.

Getting Started

In order to set up our API in rails, we need to first have some sort of materials within our websites. To quickly jump into the API development process, I’ve decided to use scaffolding for this portion of the project. We are going to generate a Paradox scaffold, which contains the title, author, and description of the paradoxes.

rails g scaffold Paradox title:string author:string description:text

rake db:migrate

Now we have quick access to all the CRUD functions for our paradoxes. The first thing we need to do after this is actually to populate the database with something. Of course, rather than having you guys finding a bunch of paradoxes, I’ve precompiled something and created a seed file for you. We are going to populate the database with this seed file instead.

db/seeds.rb

Paradox.create(title:"Buridan's Bridge", author:"Jean Buridan", description:"Imagine the following scenario: Socrates wants to cross a river and comes to a bridge guarded by Plato, who says:
Plato: Socrates, if in the first proposition which you utter, you speak the truth, I will permit you to cross. But surely, if you speak falsely, I shall throw you into the water.
Socrates: You will throw me into the water")
Paradox.create(title:"Paradox of Fiction", author:"Colin Radford", description:" Most people have emotional responses to characters, objects, events etc. which they know to be fictitious.
On the other hand, in order for us to be emotionally moved, we must believe that these characters, objects, or events, truly exist.
But no person who takes characters or events to be fictional at the same time believes that they are real.")
Paradox.create(title:"Fitch's paradox of knowability", author:"Frederich Fitch", description:"It provides a challenge to the knowability thesis, which states that every truth is, in principle, 
knowable. The paradox is that this assumption implies the omniscience principle, which asserts that every truth is known. Essentially, Fitch's paradox asserts that the existence of an unknown truth is unknowable. So if all truths were knowable, it would follow that all truths are in fact known.")
Paradox.create(title:"Paradox of hedonism", author:"Henry Sidgwick", description:"The impulse towards pleasure can be self-defeating. We fail to attain pleasures if we deliberately seek them.")
Paradox.create(title:"Meno's paradox", author:"Meno", description:"Meno asks Socrates: 'And how will you inquire into a thing when you are wholly ignorant of what it is? Even if you happen to bump right into it, how will you know it is the thing you didn't know?'
	Socrates rephrases the question, which has come to be the canonical statement of the paradox: 'A man cannot search either for what he knows or for what he does not know. He cannot search for what he knows--since he knows it, there is no need to search--nor for what he does not know, for he does not know what to look for.'")

And now run the following command to populate the database with the information above.

rake db:setup

If, at any time throughout the project, you want to add more information to the seed file, simple call

rake db:seed

to re-seed your database.

To ensure that there are data in your database, head over to http://localhost:3000/paradoxes to see if the listings are on the index page.

Configuring the Routes

Like previously described, API calls are basically just route calls to the application itself. To ensure that our current routes will not interfere with our API calls, we will utilize namespacing in rails.

Among many methods, using namespaces helps the application to separate routes in a manageable order. Based on the rails documentation on namespacing, we can create a namespace specifically for our API routes like below.

config/routes.rb

Rails.application.routes.draw do
  resources :paradoxes

  #create namespace
  namespace :api do
    resources :paradoxes
  end
end

Notice how we have two sets of paradoxes resources. With namespace :api, we now have a different set of routes for the paradoxes resources that is within the namespace, and another one that’s outside. To see this in effect, check out rake routes

Prefix Verb   URI Pattern                       Controller#Action
       paradoxes GET    /paradoxes(.:format)              paradoxes#index
                 POST   /paradoxes(.:format)              paradoxes#create
     new_paradox GET    /paradoxes/new(.:format)          paradoxes#new
    edit_paradox GET    /paradoxes/:id/edit(.:format)     paradoxes#edit
         paradox GET    /paradoxes/:id(.:format)          paradoxes#show
                 PATCH  /paradoxes/:id(.:format)          paradoxes#update
                 PUT    /paradoxes/:id(.:format)          paradoxes#update
                 DELETE /paradoxes/:id(.:format)          paradoxes#destroy
   api_paradoxes GET    /api/paradoxes(.:format)          api/paradoxes#index
                 POST   /api/paradoxes(.:format)          api/paradoxes#create
 new_api_paradox GET    /api/paradoxes/new(.:format)      api/paradoxes#new
edit_api_paradox GET    /api/paradoxes/:id/edit(.:format) api/paradoxes#edit
     api_paradox GET    /api/paradoxes/:id(.:format)      api/paradoxes#show
                 PATCH  /api/paradoxes/:id(.:format)      api/paradoxes#update
                 PUT    /api/paradoxes/:id(.:format)      api/paradoxes#update
                 DELETE /api/paradoxes/:id(.:format)      api/paradoxes#destroy

As shown above, there is one set of normal paradoxes resources, but another set with API embedded within the path names.

The API Controller

Now that we have our namespaces all set up, let’s create our Paradoxes API controller. To start off with, simply create a new api folder under the controllers directory. In this newly created folder is where all the code related to the Paradoxes API will go.

Withn controllers/api, create a new file called paradoxes_controller.rb. Note that it has the file name as the other controllers, which shouldn’t be surprising since we have the same resources under the api namespace back in the routes.

Let’s define our newly created API controller

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
	
end

Note that although this looks similar to a regular controller, we have to define the ParadoxesController to be within the Api namespace. The ruby symbol :: allows access to a constant, module, or class defined inside another class or module. In this case, we are defining the ParadoxesController to be within Api namespace with Api::ParadoxesController. To learn more about the :: symbol, take a look at this post.

Paradoxes API - Index

Let’s introduce API access by first implementing an Index call. When this endpoint is called, the user should be able to see all the Paradoxes we have. There are many format that we can return the API calls as, but for this project let’s use the JSON format. If you went through Project 1, you might have recalled that the Riot Game’s API also returns its data in JSON format.

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
  # this will return all the Paradoxes in JSON format
  def index
    render json: Paradox.all
  end
end

The code above is really simple. First we need to define what the index function will return, and it’s Paradox.all. To render all the information within Paradox.all as JSON format, simply call it as render json: Paradox.all.

Simple enough, right?

To see how this all in action, simple head to http://localhost:3000/api/paradoxes.json and you should see a list of Paradoxes all in JSON format.

Simulate an End User Call with cURL

To simulate how an end user would actually use the API, we’re going to use a neat little tool called cURL. cURL is a command line tool that, in this case, allows the transfer of data in a way a end user might transfer data. When the users are using the API, they will not be able to head to the website itself and make changes there. Therefore, there has to be a way in which the users can do all of the CRUD calls through the API.

If you’re on a MAC, cURL should already be available to you via the command line. If you’re on windows, simply head to the cURL Download Page and download the version that best matches your needs.

To call the index method with cURL, first make sure your server is running (rails s), and then try the following

curl http://localhost:3000/api/paradoxes.json

The command line should now display a list of all the paradoxes in our database like so

[{"id":1,"title":"Buridan's Bridge","author":"Jean Buridan","description":"Imagine the following scenario: Socrates wants to cross a river and comes to a bridge guarded by Plato, who says:\nPlato: Socrates, if in the first proposition which you utter, you speak the truth, I will permit you to cross. But surely, if you speak falsely, I shall throw you into the water.\nSocrates: You will throw me into the water","created_at":"2015-10-13T02:57:48.249Z","updated_at":"2015-10-13T02:57:48.249Z"},{"id":2,.......}]

Paradoxes API - Show

The show method looks very similar to the index function above. See if you can figure it out by yourself first.

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
  # this will return all the Paradoxes in JSON format
  def index
    render json: Paradox.all
  end

  # return individual Paradox in JSON format.
  def show
    paradox = Paradox.find(params[:id])
    render json: paradox
  end
end

And the cURL command is also extremely similar. Let’s see what cURL returns if we call upon a Paradox with an ID of 3.

curl http://localhost:3000/api/paradoxes/3.json

{"id":3,"title":"Fitch's paradox of knowability","author":"Frederich Fitch","description":"It provides a challenge to the knowability thesis, which states that every truth is, in principle, \nknowable. The paradox is that this assumption implies the omniscience principle, which asserts that every truth is known. Essentially, Fitch's paradox asserts that the existence of an unknown truth is unknowable. So if all truths were knowable, it would follow that all truths are in fact known.","created_at":"2015-10-13T02:57:48.257Z","updated_at":"2015-10-13T02:57:48.257Z"}

Great, let’s move on to something a bit more complicated!

Paradoxes API - Create

Retrieving and reading informations seems simply enough, but what about actually altering the contents with the database? How will the API handle that?

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
  ...
  #create and store the data through API
  def create
    new_paradox = Paradox.new(paradox_params)
      if new_paradox.save
        render json: {
          # standard HTTP code for success
          status: 200,
          message: "Successfully created a Paradox. But can you really create a Paradox..?",
          paradox: new_paradox
        }.to_json
      else
        render json: {
          # something is wrong
          status: 500,
          errors: new_paradox.errors
        }.to_json
      end
    end

    private
      def paradox_params
        params.require(:paradox).permit(:title, :author, :description)
      end
  end

Let’s break down what the create method is doing. Fist, we’re simply creating a new new_paradox. If that paradox is successfully saved, we will first render the status code. During the transfer of data, lots of things can happen. To accurately describe to the users what is happening, HTTP uses status codes to represent the state of requests. Here, I use status code 200 (OK) and status code 500 (Internal Error) to describe when a transfer is completed or not respectively. Although I used 200 and 500, feel free to look around to see if there are any other ones that better describe the current state of the transfer (maybe code 422 - Unprocessable Entity for errors?) To see a list of all available status codes, check out this site.

After the status code, we can return some sort of messages. This is just a quick message to pinpoint what exactly the transfer is doing.

Although not required, I also sent back the new_paradox as part of the JSON. This will just give the users easier access to what is actually being created. If the new paradox cannot be created, the errors will show up instead.

Now, because rails is so awesome, it automatically prevents external sources from messing with the database. In order to call the curl command on the API, we need to input skip_before_filter :verify_authenticity_token to the controller. This will allow rails to skip adding a token every time a request is called.

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
  skip_before_filter :verify_authenticity_token
  ...
  #create and store the data through API
  def create
    new_paradox = Paradox.new(paradox_params)
      if new_paradox.save
        render json: {
          # standard HTTP code for success
          status: 200,
          message: "Successfully created a Paradox. But can you really create a Paradox..?",
          paradox: new_paradox
        }.to_json
      else
        render json: {
          # something is wrong
          status: 500,
          errors: new_paradox.errors
        }.to_json
      end
    end

    private
      def paradox_params
        params.require(:paradox).permit(:title, :author, :description)
      end
  end

And now we can attempt to cURL the create method above. This cURL will look drastically different than the ones we’ve seen before, and I’ll try my best breaking it down for you.

Input

curl -i \
     -H "Accept: application/json" \
     -H "Content-type: application/json" \
     -X POST \
     -d '{"title":"My Paradox","author":"Me", "description":"Is this a Paradox?"}' \
     http://localhost:3000/api/paradoxes

Output

HTTP/1.1 200 OK 
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"983b64183f39c30b3b40242aee7f2fc7"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 7d1b5799-b529-45a5-bacc-cd9aa6f7b465
X-Runtime: 0.036273
Server: WEBrick/1.3.1 (Ruby/2.1.3/2014-09-19)
Date: Tue, 13 Oct 2015 23:48:10 GMT
Content-Length: 267
Connection: Keep-Alive
Set-Cookie: request_method=POST; path=/

{"status":200,"message":"Successfully created a Paradox. But can you really create a Paradox..?","paradox":{"id":11,"title":"My Paradox","author":"Me","description":"Is this a Paradox?","created_at":"2015-10-13T23:48:10.492Z","updated_at":"2015-10-13T23:48:10.492Z"}}

Let’s go over the tags for curl first. Unlike the first two curl commands, this one contains a lot more tags. Here, we see the -i, -H, -X, and -d tags. Let’s take a look at the Curl Doc Page and see what each tag represents.

-i tag stands for include the HTTP-header (which we’ll get to in just a bit). The HTTP-header includes a lot of things such as HTTP-version and dates.

-H tag stands for header. This tag will include whatever the tag’s extra requests into the header when sending HTTP through the server. The header field defines the operating parameters of an HTTP transaction. In the above case, we are sending in "Accept: application/json" and "Content-type: application/json" as extra headers through the HTTP transfer. The two extra headers are needed because of we need the HTTP transfer to recognize and accept JSON as the content type. To learn more about headers and how other kinds can be used, this Wiki page is really helpful.

-X is the request tag. Common requests are the four requests that are mapped to CRUD: GET, POST, PUT, and DESTROY. Normally, the GET request is assumed. Since we are invoking the create method, we need to specify that we want the POST request instead.

-d is the data that is sent through the POST request to the HTTP server. This tag allows cURL to pass the data to the server (in this case, adding data to the database).

All of the tags may seem confusing at first (I still don’t understand them fully either), but it’s pretty interesting to see how cURL deals with the different requests in a relatively simple manner.

Following the above command, head over to http://localhost:3000/paradoxes and you should be able to see the newly created paradox at the bottom of the list.

Paradoxes API - Update and Destroy

Now that you understand how cURL works, the rest of the code should come at no surprises to you.

apps/controller/api/paradoxes_controller.rb

class Api::ParadoxesController < ApplicationController
  ...
  def update
    paradox = Paradox.find(params[:id])
    if paradox.update(paradox_params)
      render json: {
        status: 200,
        message: "Successfully Updated Paradox",
        paradox: paradox
      }.to_json
    else
      render json: {
        status: 422,
        message: "Paradox cannot be Updated",
        paradox: paradox
      }.to_json
    end
  end

  def destroy
    paradox = Paradox.find(params[:id])
    paradox.destroy
    render json: {
    	status: 200,
    	message: "Successfully Deleted a Paradox."
    	}.to_json
   end

    private
      def paradox_params
        params.require(:paradox).permit(:title, :author, :description)
      end
  end

And as for the cURL inputs…

cURL Input for Update

curl -i -H "Accept: application/json" -H "Content-type: application/json" -X PUT -d '{"title":"WHAT A WEIRD PARADOX"}' http://localhost:3000/api/paradoxes/11

Output for Update

HTTP/1.1 200 OK 
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"a8c53ba5a7b8d878ecc41e8be26cf337"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 09f56f37-b7f6-41bd-88cd-a0254faf43aa
X-Runtime: 0.046140
Server: WEBrick/1.3.1 (Ruby/2.1.3/2014-09-19)
Date: Wed, 14 Oct 2015 00:12:18 GMT
Content-Length: 235
Connection: Keep-Alive
Set-Cookie: request_method=PUT; path=/

{"status":200,"message":"Successfully Updated Paradox","paradox":{"id":11,"title":"WHAT A WEIRD PARADOX","author":"Me","description":"Is this a Paradox?","created_at":"2015-10-13T23:48:10.492Z","updated_at":"2015-10-14T00:12:18.599Z"}}

cURL Input for Delete

curl -i -H "Accept: application/json" -H "Content-type: application/json" -X DELETE http://localhost:3000/api/paradoxes/11

Output for Delete

HTTP/1.1 200 OK 
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"60eeafbab0797649ad25671d711ac681"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e64db5c6-0121-4d69-adb4-f1b7255d806c
X-Runtime: 0.013715
Server: WEBrick/1.3.1 (Ruby/2.1.3/2014-09-19)
Date: Wed, 14 Oct 2015 00:15:34 GMT
Content-Length: 58
Connection: Keep-Alive
Set-Cookie: request_method=DELETE; path=/

{"status":200,"message":"Successfully Deleted a Paradox."}

And there you go! You now know how a create a bare bone API for your next project! Thanks for sticking around, see you next time.

Some Self Reflection

First and foremost, this post would not be possible without the TreeHouse tutorial on how to create an API (pretty everything I talked about in this post was taught by them.)Even though this is the bare minimum attempt to create an API, I’m still pretty excited that I could actually follow along with TreeHouse and then reproduce it.

One thing to note is that this is only the “shell” of an API construction. There are no authentications and limits, which is definitely required for any API calls. I think that’s what I’ll be looking at next time as I attempt to learn more about API construction.