HOWTO access the Gmail API with Ruby

This guide will show you how to access the Gmail API with Ruby, using the OAuth 2.0 flow for web applications. You can use the Gmail API for things like migrating email messages between accounts, performing detailed analysis of archived messages, automating sending of outgoing messages, and extraction of data from incoming messages.


Step 1: Setup your project

First you’ll need to sign in to the Google Developers Console and register your app:

You can find these pages under the “APIs & auth” menu item, and you can come back and tweak all these settings before you deploy to staging or production environments.

Step 2: Install the google-api-client gem

Add the google-api-client gem to your Gemfile:

gem 'google-api-client', '~> 0.9', require: 'google/apis/gmail_v1'

Then run bundle. The gem has a lot of dependencies, but will save you from having to write any low level HTTP client code to call the API.

Step 3: Obtain an authorization code

The first step in the OAuth 2.0 flow is to redirect the user to Google so they can sign in and consent to the access you are requesting.

If you’re using Rails your controller action might look something like this:

def redirect
  client = Signet::OAuth2::Client.new({
    client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'),
    client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'),
    authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
    scope: Google::Apis::GmailV1::AUTH_GMAIL_READONLY,
    redirect_uri: url_for(:action => :callback)
  })

  redirect_to client.authorization_uri.to_s
end

There’s a lot going on here, so let’s go through some of the details:

Step 4: Obtain an access token

The next step in the OAuth 2.0 flow is to handle the callback and exchange the temporary authorization code for an access token. If you’re using Rails your controller action might look something like this:

def callback
  client = Signet::OAuth2::Client.new({
    client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'),
    client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'),
    token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
    redirect_uri: url_for(:action => :callback),
    code: params[:code]
  })

  response = client.fetch_access_token!

  session[:access_token] = response['access_token']

  redirect_to url_for(:action => :labels)
end

Initialization of the client object is similar to before, and includes the temporary authorization code from the request params.

The call to #fetch_access_token! exchanges the authorization code for an access token, which is then put into the session to persist it between requests.

Step 5: Call the Gmail API

Now that you have an access token you can call the Gmail API itself–finally!

The callback action in the previous step finishes by redirecting to a labels action, which you can implement like this:

def labels
  client = Signet::OAuth2::Client.new(access_token: session[:access_token])

  service = Google::Apis::GmailV1::GmailService.new

  service.authorization = client

  @labels_list = service.list_user_labels('me')
end

This time the client object is initialized with the access token from the session. The service object provides methods for making calls to the Gmail API. This example calls the Users.labels: list API method, which returns a list of the user’s labels. The API response is assigned to an instance variable so it can be accessed from the view.

You can then add in a basic view to list the labels like this:

<ul>
<% @labels_list.labels.each do |label| %>
  <li><%= label.name %></li>
<% end %>
</ul>

You should now be able to test the complete flow end-to-end by starting at the redirect action. If everything is configured correctly the callback action should fetch the access token and redirect to the labels action to display a list of your labels.

Things to consider

The examples above implement the “happy path”, and don’t handle errors or exceptions which you should code for in a production ready application. Some common failure scenarios you might want to implement:

The code examples above contain some duplication in setting up the signet client. To reduce this you can either use some of the classes included in the google-api-client gem (Google::APIClient::ClientSecrets and Google::APIClient::InstalledAppFlow), or you can refactor and reorganize it as you would any other code.

You might want to consider storing the tokens in some kind of datastore instead of the session, depending on the needs of your application.