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.