DEV Community

loading...
Cover image for Writing an API in Rails 6

Writing an API in Rails 6

alvinqingxing profile image Alvin Lim ・6 min read

In the final stage of my coding bootcamp, when I had become familiar with Ruby on Rails, one project I wanted to do was to write my own API.

APIs (Application Programming Interfaces) are widely used on the internet for computers to send data to one another. Unlike a standard web page, where the server sends a bundle of HTML, CSS, and JavaScript to create a view which is consumed by a human audience, with APIs it is machines communicating with one another.

So if you are viewing a web application that requires data from an API, your browser will send the request to the API, get the requested data back, and then render that data into a format that is suitable for you, a human, to view.

Hence, instead of HTML and CSS, APIs send data in more machine-friendly formats such as JSON, YAML, or XML.

For my API, I wanted to do something fun. My coding class had previously learned how to use Faker to seed our databases with mock information, and I recalled that one of Faker's libraries consisted of Chuck Norris jokes. I decided to go with this and create an API that sends out Chuck Norris jokes.

Let's build the API! First I create the directory and go into it:

$ mkdir chuck-norris-api
$ cd chuck-norris-api
Enter fullscreen mode Exit fullscreen mode

Once in the directory, I instruct Rails to generate the app:

$ rails new chuck-norris-api --api --database=postgresql
Enter fullscreen mode Exit fullscreen mode

The --api option (which is available in Rails 6) tells Rails that you don't want it to generate a full app -- only enough for an API to run. I set the second option --database=postgresql as I will be using Heroku to host the API, and Heroku does not support Rails' default SQLite database.

Once the app is generated, I open my code editor and edit the Gemfile located at the root of the directory. I add gem 'faker' and uncomment gem 'rack-cors':

Alt Text

As I explained earlier, I will be using Faker to populate my database with Chuck Norris jokes, while Rack CORS is the middleware I need to ensure that the front-end I will be building to display the jokes will be able to receive the jokes from the API (more on that later).

I then run bundle install in the terminal to install the new gems. Once that's done, it's time to build out the app's MVC!

First, the models. In this case, there's only one: the jokes. Each joke has one field: its content. In the terminal I instruct Rails to generate the model:

$ rails g model Joke content
Enter fullscreen mode Exit fullscreen mode

This create the following migration:

Alt Text

In the terminal I instruct Rails to run the migration:

$ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

This creates the Jokes table in the database:

Alt Text

The database can now be populated with the Chuck Norris jokes. In seeds.rb I instruct Rails to retrieve a hundred jokes from Faker's Chuck Norris library:

Alt Text

In the terminal, I then instruct Rails to run seeds.rb and populate the database:

$ rails db:seed
Enter fullscreen mode Exit fullscreen mode

Now that the model and database are sorted, I can proceed with the controller and view (the remaining two-thirds of the app's MVC). In the app\controllers folder I create the nested folders api\v1. In the v1 folder I create the jokes_controller.rb file:

Alt Text

In my controller, I only allow the user to perform two actions: get all jokes, and get one particular joke. I do not want the user to edit or delete existing jokes or to add new jokes. This is for the simple reason that I do not want to have to keep checking the database for inappropriate content once the API is deployed. Hence I do not include these remaining CRUD operations in the controller.

As with the controller, I add nested folders (api\v1\jokes) in the app\views folder. The api\v1\jokes folder has two files corresponding to the two controller actions. The first is the index.json.jbuilder file which will build and return a JSON file containing an array of all one hundred Chuck Norris jokes when a request for all jokes is received. In particular, I instruct the JSON builder to only include the content of each joke in the array, and leave out the other fields such as the id numbers of the jokes:

Alt Text

The second is the show.json.jbuilder file which will return the joke with the particular id sent by the controller. As with the index file, I instruct the JSON builder to only include the content of the joke in the JSON file that is sent back to the user:

Alt Text

Now that the MVC are done, we can now proceed to the routes. In the config\routes.rb file I specify the nested api and v1 namespaces that correspond to the nested api\v1 folders where the controller is housed, and I also specify that the only actions offered by the API are the index (get all jokes) and show (get one joke) actions, rather than the full suite of CRUD operations:

Alt Text

There is one final step that I need to do before I can deploy the app to production. It is a security feature of modern web browsers that, by default, they do not retrieve data from APIs if the domain of the requesting web page is different from the domain of the API. This will only be allowed if the API specifically allows web pages hosted on that particular domain to retrieve its data.

The obvious way to get around this restriction is to host the web page on the same domain as the API. But what is the fun in that? For this project, the API will be hosted on Heroku, and the web page which I will be displaying the jokes will be hosted on GitHub Pages. This is where the Rack CORS gem that had been installed earlier comes in. In the config\initializers\cors.rb file I specify that my GitHub Pages domain is to be allowed:

Alt Text

While it is certainly possible to set origins '*' in cors.rb and allow any website to access the API, this will effectively bypass the security protocol. So I will be following the best practice and curate which domains are allowed to use the API. It should be noted that this is a security feature of web browers. As we shall see, if you use a tool like curl or Postman to access the API, this cross-domain security block will not apply.

The app is now ready to be deployed! It can be accessed at https://rails-chuck-norris-api.herokuapp.com and as noted, you can use curl from your terminal to retrieve all the jokes:

curl -s https://rails-chuck-norris-api.herokuapp.com/api/v1/jokes | jq
Enter fullscreen mode Exit fullscreen mode

... or any one of the one hundred jokes. Here curl retrieves joke #99:

curl -s https://rails-chuck-norris-api.herokuapp.com/api/v1/jokes/99 | jq
Enter fullscreen mode Exit fullscreen mode

But as noted, I have created a nice front-end at GitHub Pages to retrieve and display the jokes. The source code can be viewed here, but the essential part is this JavaScript code which generates a random number between 1 and 100 and retrieves the joke with that id from the API:

Alt Text

Chuck Norris has the final word:

Alt Text

I found this to be a really fun project to do, and I hope it will inspire you to create your own API!

Discussion (0)

pic
Editor guide