Since we've launched our platform, we're building to enhance our DNSimple experience. We built a Slack application, a Telegram bot, and other fun experiments.

Good news: you can build your own addon too!

This is a step-by-step guide to write it with our Ruby client and Hanami.

Setup

Before we start, please make sure to have:

DNSimple OAuth app

Login into your DNSimple Sandbox account, and create an OAuth application by entering http://localhost:2300 as Homepage URL and http://localhost:2300/auth/dnsimple/callback as Authorization Callback URL.

Screenshot showing the form for a new DNSimple OAuth app

Each OAuth app has its own Client ID and Client Secret, please save this information in a safe place, you're going need it soon.

Screenshot showing a DNSimple OAuth app

DNSimple test domain

Register a test domain, so you can use it for this tutorial. Please note that this is a testing environment: no money will be charged, and the domain won't resolve for real.

Project skeleton

Now we can setup our project. Install Hanami (gem install hanami) and then run hanami new hello_domains --test=rspec. Voilà we have a new skeleton project to work with!

Enter in the newly created directory (hello_domains) and edit .env.development, adding the following lines:

DNSIMPLE_CLIENT_ID="..."
DNSIMPLE_CLIENT_SECRET="..."
DNSIMPLE_BASE_URL="https://api.sandbox.dnsimple.com"

Please replace the dots with the Client ID and the Client Secret from the step above.

Before we start the web server of the project, we need to add a few gems to our Gemfile:

gem 'dnsimple'
gem 'omniauth'
gem 'omniauth-dnsimple', github: 'dnsimple/omniauth-dnsimple'

We can now run bundle and then bundle exec hanami server. This last command will start the web server and the application will be available at http://localhost:2300.

Implementing the signup

Our project is up and running, but it isn't so useful for now. Let's implement the signup, so we can recognize the DNSimple user that is using our addon.

OAuth setup

The authentication is made via OAuth 2 (DNSimple is a provider). For the purpose of this example we can use omniauth and omniauth-dnsimple gems.

The authentication flow requires HTTP sessions to be enabled, we need to edit apps/web/application.rb and uncomment the following line:

sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']

While still in apps/web/application.rb, we should mount the OmniAuth Rack middleware. This is a generic stack where we can add support for many providers at once. For instance, an application may support GitHub and Twitter providers.

In our case, we want just add DNSimple strategy (in OmniAuth terminology). Please note that this strategy is provided by omniauth-dnsimple gem.

middleware.use OmniAuth::Builder do
  provider :dnsimple, ENV['DNSIMPLE_CLIENT_ID'], ENV['DNSIMPLE_CLIENT_SECRET'], sandbox: true
end

These two environment variables (ENV) are the same we set up before in .env.development.

Signup action

With the OAuth setup done, we can now create an action to let the users of the addon sign in. Run the following command:

bundle exec hanami generate action web sessions#create --method=GET --url=/auth/:provider/callback --skip-view

This will create a few files. The most important file is: apps/web/controllers/sessions/create.rb.

module Web::Controllers::Sessions
  class Create
    include Web::Action

    def call(params)
      data = extract_oauth_params(params.env)
      user = UserRepository.new.find_or_create_by_uid(data['uid'], data)

      session[:user_id]    = user.id
      session[:user_email] = user.email

      redirect_to "/"
    end

    private

    OMNIAUTH_AUTH = 'omniauth.auth'.freeze

    def extract_oauth_params(env)
      uid   = env.dig(OMNIAUTH_AUTH, 'uid')
      info  = env.dig(OMNIAUTH_AUTH, 'info')
      token = env.dig(OMNIAUTH_AUTH, 'credentials', 'token')

      info.merge('uid' => uid, 'token' => token)
    end
  end
end

The first step is to extract the OAuth data from the Rack environment, then we can retrieve a reference to the user (if it doesn't exist yet), and finally we can store its ID in the session.

Before we try we need to implement a few missing pieces.

First of all, we need the home page:

bundle exec hanami generate action web home#index --url=/

Let's edit the application template (apps/templates/application.html.erb):

<!DOCTYPE html>
<html>
  <head>
    <title>Hello Domains</title>
    <%= favicon %>
  </head>
  <body>
    Hello <%= session[:user_email] || "Stranger" %>!
    <%= yield %>
  </body>
</html>

Now we need a model to persist our data.

User model

bundle exec hanami generate model user

This command will generate a bunch of files. The first to edit is the database migration. It's located under db/migrations and its file name starts with a timestamp (e.g. db/migrations/20161209080441_create_users.rb).

Hanami::Model.migration do
  change do
    create_table :users do
      primary_key :id

      column :uid,   Integer, null: false, unique: true
      column :type,  String,  null: false
      column :email, String,  null: false
      column :token, String,  null: false

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false
    end
  end
end

After we edit the migration, we can create the database (if it doesn't exist) and migrate it:

bundle exec hanami db prepare

Now we need to implement the find or create method used by the action above:

# lib/hello_domains/repositories/user_repository.rb
class UserRepository < Hanami::Repository
  def find_or_create_by_uid(uid, data)
    by_uid(uid) or create(data)
  end

  def by_uid(uid)
    users.where(uid: uid).one
  end
end

Manual test

Let's see if it works. Start the server (bundle exec hanami server) and then visit http://localhost:2300/auth/dnsimple. You should see a DNSimple authorization page: accept and proceed. Now DNSimple has redirected you back to the addon, where you should see a message like Hello luca@example.com!.

Great! You've just implemented the authentication process.

Fetch the domains

Now that we have connected our addon with DNSimple, we can use our official Ruby client to fetch our domains.

Please edit apps/web/controllers/home/index.rb with the following contents:

module Web::Controllers::Home
  class Index
    include Web::Action
    expose :domains

    def call(params)
      @domains = []

      return if session[:user_id].nil?
      current_user = UserRepository.new.find(session[:user_id])

      client = Dnsimple::Client.new(base_url: ENV['DNSIMPLE_BASE_URL'], access_token: current_user.token)
      @domains = client.domains.list_domains(current_user.uid).data
    end
  end
end

At this point we want to show the domains in an HTML table. For this purpose we need to edit the template: apps/web/templates/home/index.html.erb

<% if domains.empty? %>
  <p>Sorry, there aren't domains. Please <%= link_to "sign in", "/auth/dnsimple" %></p>
<% else %>
  <table>
    <tr>
      <th>Name</th>
    </tr>
    <% domains.each do |domain| %>
      <tr>
        <td><%= domain.name %></td>
      </tr>
    <% end %>
  </table>
<% end %>

Final test

Start the server again and visit http://localhost:2300, now you should see the domains from DNSimple.

Wrapping up

We have built a "quick and dirty" DNSimple addon in a few minutes. The complete (and working) example of this article is available on GitHub: hello-domains-hanami.

If you looking for other frameworks, we built several hello domains apps for Ruby on Rails, Phoenix, and Node.

Conclusion

Our platform is a powerful tool to integrate and enhance the DNSimple experience. You can build addons to monitor your domains, connect them with hosting solutions, run security checks, and even to improve your email deliverability.

The sky is the limit! 🚀