API Calls in Rails: from Basic to Better and some important considerations
Grabbing data from and API in Rails is not overly complex. Doing it correctly, and with a little security, is a tougher task. Let’s first take a look at how to get it running quickly, a ‘proof on concept’ for bringing in the data, then let’s look at some things to consider to make improvements.
The basic steps for an API call are:
- Find out what URL to call to and what pieces of data you will need to give them
- Get yourself an API key (likely, directly from that API’s site)
- Set up a method that fetches from that url/api
- Parse your data into something you would like to use in your app (JSON?)
- Turn the pieces of that parsed data into variables that you can work with
For this example, let’s use Mountain Project’s API. Mountain Project is a site which lists rock climbing routes all over the world and gives information about them. Their API is simple to use and will give you such information as the name of a route, where it is located, and how hard it is. If you want to practice with this same API you can find it here. On the left of the page, you can see the url to make the API call. The only pieces of data that are needed are a route-id number and an API key. Route id’s can be grabbed from the url of any specific route on their site. For this example, I pulled the number 105717718 from this page which is the specific page for Moonlight Buttress. (For those of you who are climbing fans, this is one of the routes that first catapulted Alex Honnold to free solo fame.)
On the right side of the MP API page, there is a button to click to sign up for an API key. Once we do that, we have everything we need to make out first call!
To make our first call, we’ll stay simple. Let’s create a view page and head there. In the view, we can make a front-end API call with JS. We’ll need script tags as this is an
html.erb file. Here’s what I set up:
If we look at our list of 1–5 from above: we found the api url, inserted the required data (including the api key, which is everything that comes after the
key= on line 3; yours should be different than mine and in fact, the key in this example is made up as it is important to protect your api keys; see below for more thoughts on this), we made a fetch call to that API (line 3), we parse the data into JSON (line 5), and now we have a JSON object with our data (which we creatively named
myJson). Since we are console logging this object (line 8), we can open up dev tools and inspect the object from the browser if we run this view in our local server. In this case, we are receiving:
Last, but not least, we need to create some variables so that we can use this information. I would like to use the name and the difficulty of this climb, so let’s make variables for those two.
Here, the elements’ innerHTML is set to the JSON response’s values for name and rating and we display those to the viewer.
This type of API call is easy to set up and easy to execute. But, it has some serious limitations. First and foremost, this is not the Rails way. Having all of this happing in one view means that our different concerns are highly mixed, highly dependent, not reusable, and highly public. The highly public piece is particularly challenging for API calls because there is no way to obscure your API key from a user if it is left in the front end.
Backend API calls with Rails are also fairly simple, and much preferable. To move this call to the backend, we need to complete the same steps, but in Ruby/Rails. We already have our data and our API key, so now it is just the fetch and the variables that we need. First, we would delete all of this code from our view. Then, we would move to our controller action that is linked to this view. There, one of the easiest ways to make an API call in Rails is to use the HTTParty gem
gem 'httparty’. In my case, I am using
gem 'httparty', ‘~> 0.13.7’ , but latest versions can be found online here. With this gem installed in our
gemfile and a
bundle instal run in the terminal, the call is as simple as
We use the same url with data and api key to make the call, and we set the returned object equal to the variable
route. Next, we call
parsed_response on that variable to return our JSON and we can pull our name and rating attributes directly form there. By setting then equal to instance variables (lines 3 & 4) we have access to them in the front end and we can set up something like this in the corresponding view:
Great! Now we are calling our API from the backend. Our variables can be passed to multiple views, our concerns are separated, and our API key is no longer visible to all of our users via the browser! A big win, to be sure.
Well, our app is working. I guess we should just push it up to Github, right?
Unfortunately, no. We are not yet ready. Although we have pushed the API calls to the backend and therefore a user could not find our secret API key directly through the browser, they will be able to find it in our codebase. So, our next step is to encrypt!
Encryption of keys in Rails works through the
config/credentials.yml.enc file. Because it is an encrypted file, it cannot be edited directly. Instead, you need to bring up a temporary version of the file through the command line, make edits there, and then save them to be encrypted. The terminal command will bring the file up in your favorite editor if you direct it to do so. In this case, I am using Atom, so I use the command
EDITOR="atom --wait" bin/rails credentials:edit. This will stall your terminal and bring up an editable file in your terminal. It should look like this:
You should add your API key as the value in a key/value pair and give it a name. Above, I named my key
mountain_project_secret so that I would remember which API key is saved there in case I want to call other APIs in this app. To un-stall your editor, simply save and close this file. With that file saved, we have access to a global variable
Rails.application.credentials.mountain_project_secret. Obviously, the last call of that set will be the name that you have selected in your file to serve as your key. Now, our API call looks like this:
Now, we are ready to go. Our development app is making backend API calls with an encrypted API key which is not visible in either our front end or our codebase. Perfect! But… how will our production environment know how to decrypt our encrypted API key?
The API key in the
credentials.yml.enc file is decrypted via the contents of the
master.key file. By default (and it should stay this way), this file is included in our
Therefore, we need to give this key directly to our production environment via an environment key. For this example, we are hosting on Heroku. Setting this key for Heroku is simple.
First, push your app to Heroku and migrate the db. If we only do this and then try to go to the page that is sourcing our API call variables, Haroku will return this error:
In the terminal, run:
heroku config:set RAILS_MASTER_KEY=1234567 where 1234567 is the string that is contained in your
config/master.key file. Now, you should be up and running!