sborrazas

Sebastian Borrazas

I'm a full stack engineer from Montevideo. Love functional programming, the web and Erlang.

Building an API Client

Most of the times in my web project I'm building API clients. These clients will probably use other clients, and therefore it becomes other API clients wrappers. It doesnt really matter whether it is for an internal API like our DB storage or some external API like twitter. We shold always build wrappers around them. By doing so an API client can really improve our application design and file structure.

This pattern for building clients was inspired by the Octokit Gem from @pengwynn (an amazing developer).

Here's an example of how I would create a client on some project of mine:

# lib/myapp/client.rb
require 'myapp/client/notifications'
require 'myapp/client/publications'

module MyApp
  class Client

    include MyApp::Client::Notifications
    include MyApp::Client::Publications
    # ..

    attr_reader :user

    # Initialize a Client instance
    #
    # @param [User] user
    #   the user responsible for triggering the client actions
    def initialize(user)
      @user = user
    end

  end
end

# lib/myapp/client/publications.rb
module MyApp
  module Client
    module Publications

      # Creates a publication
      #
      # @param attributes [Hash]
      #   the new publication attributes
      #
      # @raise [MyApp::InvalidRecordError]
      #   if any validation errors occur
      #
      # @return [Publication]
      def create_publication(attributes)
        form = Forms::Publication.new(attributes, context: :create, user: user)
        form.save
      end

    end
  end
end

This is a (shortened) sample Sinatra application making use of this client:

# webapp/controllers/publications.rb
require 'json'
require 'myapp/client'

module WebApp
  # WebApp main Sinatra controller
  # You should probably add this on some other controller
  class Main < Sinatra::Base

    helpers do
      def current_user
        MyApp::User.get(session[:user_id])
      end

      def client
        @client ||= MyApp::Client.new(current_user)
      end
    end

    before do
      content_type :json
    end

    get '/publications' do
      client.publications(params[:filters]).to_json
    end

    post '/publications' do
      client.create_publication(params[:publication]).to_json
    end

    error MyApp::InvalidRecordError do
      status 422 # Or w/e
      ex = env['sinatra.error']
      { errors: ex.errors }.to_json
    end

  end
end

This is an example of how I would elaborate a Ruby application. It might not be the best approach, but it works for me!

The only downside I found with this way of implementing clients is that is it rather hard to test. To test each client extension (Publications, Notifications) you need to create a fake class which includes it. If you have any suggestions on how to do this in a better way, please let me know! I'm dying to know!

You should also check @pengwynn blogpost regarding building these types of clients, it was of great help to me.

Please submit a pull request on this blog or tweet me if you have any suggestions/improvements, I would really appreciate it.