How To Use an API with Ruby on Rails (Beginner’s Guide)

On August 15, Ruby on Rails released its version 6, making even more features available to developers. The 13-year-old framework gives you a plethora of tools out of the box, which allows building complete applications in a very short time. With adapters for almost all database systems available, you don’t need much else.

If, however, you need to add more functionality to your services, you’ll eventually need to make use of an API. For example, upon signing up users, you could use Twilio’s Verify Phone Number API to validate their contact details. You could also use an IP Geolocation service to find out where users are, and provide a more personalized experience. The possibilities are endless. You can be sure most of the services you use on a day-to-day basis use some third-party API to add value to their products.

What is an API?

An API, shorthand for Application Programming Interface, is a broad term that refers to one form or another of allowing components to communicate with each other. In the context of this article, you can think of it as a set of URLs you can request from your Rails app to get information about something, trigger some action, process a payment, etc.

Why would you want to use Rails to consume an API? Ruby’s nature makes it quite easy to use external services. In comparison to Node.js, for example, there’s no need to use promises or callbacks, so your code ends up being more readable and maintainable. Plus, RapidAPI provides ready to go examples to get started.

RESTful APIs

If you’ve heard about APIs, by now you’ve probably also heard the term “RESTful API”. REST refers to a set of rules that an API should follow to call itself RESTful. Among others, these are:

  • Client-Server Architecture: there should be clear differences or boundaries between the client and the server. This refers mostly to the separation of concerns, where the client is in charge of displaying data, where the server is in charge of storing it, in simple terms.
  • Statelessness: this simply means the server does not store any information about the client, and every request must contain all the necessary details to complete. This doesn’t mean RESTful APIs can’t allow authentication, it only means that whatever information is needed for this should be handled by the client and submitted every time.

In REST, there’s this concept of collections and members. Members belong to a collection, and each has endpoints that represent them. For instance, if you think of a Blog, a Post would be a member of the Posts collection:

Collection Member
Name Posts Post
Endpoint example.org/posts example.org/posts/1

Notice how the member endpoint has the post id (1 in this case).

Interacting with an API

For both collections and members, you can perform actions on them. By actions we mean, for example, creating, reading, updating or deleting (the acronym CRUD might ring a bell). Actions are identified using different HTTP request methods. You’ve probably already heard about at least some of them. More often than not, RESTful APIs make use of only a couple of them:

  • GET: requests the server to return either a collection or a single item of a collection, depending on whether this action was performed on a collection endpoint or a single item endpoint.
  • POST: when POSTing to a collection endpoint, you’re requesting the API to create a new resource using the payload you’re submitting. In general, this action is only performed on collections and not on items.
  • PUT: this action is regularly used to update a resource, that is, to change some or all values of it.
  • DELETE: like the name suggests, this action instructs the server to delete a resource or a collection.

There are other actions less commonly used, like PATCH. Strictly speaking, the PUT action should be used to update a whole resource, while PATCH only to update parts of it. However, in a lot of cases, APIs tend to either only use PUT or just use both indistinctively, and it’s better to read the documentation rather than to make assumptions.

Prerequisites

Before we get started, you’ll need:

  • Ruby
  • Ruby on Rails
  • A RapidAPI account

Installing Ruby and Ruby on Rails depends slightly on which platform you’re using. Please refer to the official documentation for installing them. For Ruby, you can use this guide on their website, whereas for Rails you can use their official installation guide. In order for you to understand this guide, you’ll need some basic knowledge of Rails and HTML. We’ll be using Ruby on Rails version 6, but version 5 will do just fine for this tutorial.

You will also need a RapidAPI account. In order to sign up, head over to rapidapi.com and click the sign up button, or just log in if this isn’t your first time around!

RapidAPI Free API Key

How to Connect to an API with Ruby?

Before getting started, let’s go through some initial steps on how to connect and use an API. Using RapidAPI, we can find and test different APIs easily and quickly. The great thing about this is that it allows you to play around with the different endpoints without having to set up anything. This can quickly give you an idea of how the API works, and what you can get from it.

For example, let’s take the REST Countries API. This simple service provides you with lots of information about a specific country. Plus, you can use it to search for countries (think, an autocomplete box), or get a country by its currency, language or calling code. Just as an example, let’s try finding a country by language. Select the Search by language endpoint in the list to the left:

Rest Countries v1 API Search by Language endpoint

Then, input es (or the two-letter code of the language you’d like to search for). Now, just click on the Test Endpoint button. On the right, you’ll see the results.

Rest Countries v1 Search by Language

You’ll notice you get a great deal of data about every country, from its currency to a list of countries it borders with. Feel free to play around with the other endpoints, as we’ll be using this API in the application we’ll be building.

Building a Simple Travel Planner

To get a good idea of how you can use one or more APIs in your Rails applications, we’ll be building a very simple travel planner. This app will allow the user to enter a country of destination and will show them some key details about it (the currency and timezone, for example), plus some weather information.

1. Create a New App

To get started, create a new application using rails new:

rails new travel-planner --skip-active-record

Notice we’re skipping ActiveRecord, since our application won’t be using a database. If you intend on building something from this tutorial, you may skip that modifier. We’ll be adding Bootstrap to our Gemfile to make our UI look nice. Follow the instructions on their website to get started. Adding Bootstrap is obviously not required, so skip this step if you’re just in this for the code.

gem 'bootstrap', '~> 4.3.1'

For calling the APIs, we’ll be using the excon gem, but feel free to use any networking gem you’re most comfortable with. Add this to your Gemfile as well:

gem 'excon'

Now run bundle install to fetch them.

2. Add the First View

The first thing our users will see is going to be a form where they can enter a country they want to visit. Using this, we’ll find out what the capital of that country is and show them some information about that. Let’s start by creating the controller and the view for it:

# app/controllers/travel_controller.rb
class TravelController < ApplicationController
  def index
  end
end

A simple controller which will just render this view:

<!-- app/views/travel/index.html.erb -->
<div class="container">
  <div class="row">
    <div class="col-lg-12 mt-5">
      <div class="mx-auto mt-5" style="width: 400px">
        <%= form_with(url: search_path, method: 'get', local: true) do %>
          <div class="form-group">
            <%= label_tag :country, 'Search for a country '%>
            <%= text_field_tag :country, nil, placeholder: 'Eg. Germany', class: 'form-control' %>
          </div>

          <%= button_tag 'Search', class: 'btn btn-success btn-block' %>
        <% end %>
      </div>
    </div>
  </div>
</div>

There’s a lot more here! Most of it is just Bootstrap styles, so don’t panic. Let’s go through it. We’re basically just creating a form that will send the user to our search_path with the country they’ve typed. local: true denotes we don’t want to use AJAX for this form (which is the default). Then we just specify a label and a text field for the user to type in. Lastly, we add a button to submit the form.

Before we can continue, we’ll need to add some routes to our routes file:

# config/routes.rb
Rails.application.routes.draw do
  root to: 'travel#index'

  get '/search' => 'travel#search'
end

We’re adding a root route (the default route) pointing to our TravelController’s index method, plus an extra route where we’ll be doing our API calls and showing the user the results. This is the search_path route I mentioned above. Now, when the user goes to our website, they will be presented with the form we just created.

3. Fetching Data from the APIs

Once a user sends the form, we’ll have to search for the country (and check if it exists). After that, we can use the country data to fetch the weather in the capital and display that to the user. In case we can’t find any results, we’ll just send the user back and show an alert. Let’s handle the latter first:

# app/controllers/travel_controller.rb
# ...
def search
  countries = find_country(params[:country])

  unless countries
    flash[:alert] = 'Country not found'
    return render action: :index
  end

  # ...
end

In this action, we’re finding the country using the country parameter the user submits via the form. If we get no results from the API call, we’ll set an alert and render our index action to show the form again. This requires us to tweak the form a bit so that the alert displays for the user. Add this to the form:

<!-- app/views/travel/index.html.erb -->
    
    <!-- ... -->
    <div class="col-lg-12 mt-5">
      <% if flash[:alert] %>
        <div class="alert alert-warning"><%= flash[:alert] %></div>
      <% end %>

      <div class="mx-auto mt-5" style="width: 400px">
    <!-- ... -->

This way, the user will see something like this when they enter a country we can’t find:

Rest Countries v1 Country does not exist RapidAPI

Also, you’ll probably have noticed that we’re calling some find_country method in our action. Let’s define that:

# app/controllers/travel_controller.rb
  private

  def request_api(url)
    response = Excon.get(
      url,
      headers: {
        'X-RapidAPI-Host' => URI.parse(url).host,
        'X-RapidAPI-Key' => ENV.fetch('RAPIDAPI_API_KEY')
      }
    )

    return nil if response.status != 200

    JSON.parse(response.body)
  end

  def find_country(name)
    request_api(
      "https://restcountries-v1.p.rapidapi.com/name/#{URI.encode(name)}"
    )
  end

Lots going on here! First, we define a private and generic method which we can use to fetch data from our APIs. Both calls we’ll define are very similar, so it’s good practice to extract all the similar code to a single method for easier maintenance.

This method performs a GET request to the url we specify and sends our RapidAPI Key (which is grabbed from our environment) and the host of the URL (which RapidAPI uses to identify you as a developer). After that, the method parses the response and converts it to a ruby object. In case the API returns something other than a status 200, we’ll just return nil.

Our find_country method is fairly simple: it just uses the REST Countries API to get data about a country. We need to URL-encode the query before sending it, otherwise, we don’t get any meaningful results.

4. Adding Weather and Putting it all Together

Our search action is not yet complete. We still need to fetch the weather info for our user. Let’s add some more to the action, so it ends up looking like this:

# app/controllers/travel_controller.rb
  def search
    countries = find_country(params[:country])

    unless countries
      flash[:alert] = 'Country not found'
      return render action: :index
    end

    @country = countries.first
    @weather = find_weather(@country['capital'], @country['alpha2Code'])
  end

And let’s define the find_weather method as well:

# app/controllers/travel_controller.rb
  def find_weather(city, country_code)
    query = URI.encode("#{city},#{country_code}")

    request_api(
      "https://community-open-weather-map.p.rapidapi.com/forecast?q=#{query}"
    )
  end

In order to fetch weather information, we’re using the Open Weather Map API. This API has a forecast endpoint which required the city and country. We can get this from the country data we fetched from the other API. Now, to put this all together, we’ll need a view:

<!-- app/views/travel/search.html.erb -->
<div class="container">
  <div class="row mt-5">
    <div class="col-lg-6">
      <h4>Country info</h4>
      <dl>
        <dt>Country</dt>
        <dd><%= @country['name'] %></dd>

        <dt>Capital</dt>
        <dd><%= @country['capital'] %></dd>

        <dt>Currency</dt>
        <dd><%= @country['currencies'].join(', ') %></dd>

        <dt>Timezone(s)</dt>
        <dd><%= @country['timezones'].join(', ') %></dd>
      </dl>
    </div>

    <div class="col-lg-6">
      <h4>Weather Info</h4>

      <dl>
        <% @weather['list'].each do |weather| %>
          <dt><%= weather['dt_txt'] %></dt>
          <dd>
            <strong>Min</strong> <%= to_fahrenheit weather['main']['temp_min'] %>
            <strong>Max</strong> <%= to_fahrenheit weather['main']['temp_max'] %>
          </dd>
        <% end %>
      </dl>
    </div>
  </div>
</div>

This view looks like a lot, but it’s not that complex after all. We’re grabbing some key information about the country and displaying that on the left, and, on the right, we’re simply going through the weather forecast. We created a to_fahrenheit helper, which is needed to convert the temperature we fetched from Kelvin to Fahrenheit:

# app/helpers/temperature_helper.rb
module TemperatureHelper
  def to_fahrenheit(temp)
    # Kelvin * 9/5 - 459.67
    fahrenheit = (temp.to_f * 9.fdiv(5) - 459.67).round(2)

    "#{fahrenheit}ºF"
  end
end

This saves us some code in our view and is considered good practice: you want to avoid too much application logic in your templates.

5. Testing your App

Give it a go! Run this command on your terminal to start your server up:

RAPIDAPI_API_KEY=your_api_key_here rails server

Remember to insert your RapidAPI Key in the command. You should now be able to go to http://localhost:3000, enter a country name and get some data about it plus the weather forecast:

Use an API with Ruby on Rails RapidAPI

Conclusion

This tutorial should’ve given you a basic overview of what a REST API is and how you can leverage it in your Ruby on Rails apps. You should now have some basic knowledge of how to fetch data from a web service. We recommend you play around with different APIs offered at RapidAPI.

As an exercise, you could extend this particular example we created by adding an integration to the SkyScanner API and fetch available flights to the country the user searched for.

Have an API you want to add to our API Marketplace?

#DIV_1 {
bottom: 0px;
box-sizing: border-box;
color: rgb(10, 10, 10);
cursor: pointer;
float: left;
height: 95px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(10, 10, 10);
text-size-adjust: 100%;
top: 0px;
width: 270px;
column-rule-color: rgb(10, 10, 10);
perspective-origin: 135px 47.5px;
transform-origin: 135px 47.5px;
caret-color: rgb(10, 10, 10);
background: rgb(0, 140, 220) none repeat scroll 0% 0% / auto padding-box border-box;
border: 1px solid rgb(227, 224, 224);
border-radius: 0 0 4px 4px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 0px 0px 15px;
outline: rgb(10, 10, 10) none 0px;
}/*#DIV_1*/

#A_2 {
box-sizing: border-box;
text-size-adjust: 100%;
perspective-origin: 0px 0px;
transform-origin: 0px 0px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
}/*#A_2*/

#DIV_3 {
bottom: 0px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
float: left;
height: 63px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 0px;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 31.5px;
transform-origin: 35px 31.5px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
border-radius: 5px 5px 5px 5px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 15px 5px 15px 15px;
outline: rgb(0, 0, 238) none 0px;
}/*#DIV_3*/

#IMG_4 {
bottom: -35.0781px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
display: block;
height: 66.5781px;
left: 35px;
max-width: 100%;
position: absolute;
right: -35px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 31.5px;
vertical-align: middle;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 33.2813px;
transform: matrix(1, 0, 0, 1, -35, -33.5);
transform-origin: 35px 33.2813px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
outline: rgb(0, 0, 238) none 0px;
}/*#IMG_4*/

#DIV_5 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
float: left;
height: 46px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 168px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 84px 23px;
transform-origin: 84px 23px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 22px 0px 0px 5px;
outline: rgb(255, 255, 255) none 0px;
}/*#DIV_5*/

#SPAN_6 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
width: 150px;
height: 27px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 107.047px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 53.5156px 13.5px;
transform-origin: 53.5156px 13.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 18px / 27px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_6*/

#SPAN_7 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
height: 19px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 110.188px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 55.0938px 9.5px;
transform-origin: 55.0938px 9.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 13px / 19.5px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_7*/

Related Links

Ruby Tutorials

The post How To Use an API with Ruby on Rails (Beginner’s Guide) appeared first on Last Call – RapidAPI Blog.

Source: RapidAPI