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
Once in the directory, I instruct Rails to generate the app:
$ rails new chuck-norris-api --api --database=postgresql
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'
:
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
This create the following migration:
In the terminal I instruct Rails to run the migration:
$ rails db:migrate
This creates the Jokes table in the database:
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:
In the terminal, I then instruct Rails to run seeds.rb
and populate the database:
$ rails db:seed
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:
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:
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:
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:
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:
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
... 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
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:
Chuck Norris has the final word:
I found this to be a really fun project to do, and I hope it will inspire you to create your own API!
Top comments (0)