Ruby on Rails - How to Build a Robust JSON API client with Ruby- ruby on rails tutorial - rails guides - rails tutorial - ruby rails



Building a robust JSON API client with Ruby:

  • If you are building a Ruby on Rails site and expending a famous API, chances are there's a gem for it and it's as basic as a couple of lines of code to inquiry and return what you want.
  • But then again, you could be bringing low-quality pearl code into your application, a considerably greater library than your utilization case requires, or code you simply don't see well.
  • Instead of pulling in 60k of Ruby, you may have the capacity to construct your own in 60 lines. On the off chance that it's a littler administration or a private API that was fabricated only for you, you likely need to roll your own API customer at any rate.
  • When expected to incorporate an administration for demonstrating test photos on my camera rental site, I manufactured an extremely straightforward JSON API customer utilizing HTTP arty in Ruby.
ruby on rails tutorial tags - ruby , rail , ruby on rails , rail forum , ruby on rails tutorial , ruby tutorial , rails guides , rails tutorial , learn ruby

Testing the API

  • Nothing is more frustrating than wasting time debugging an API only to discover that your API key was invalid or the failure was otherwise on the side of the API server.
  • In my case, the API maintainer gave me the following test API call:
http://www.wikitechy.com/rest/?method=list_photos&api_key=[REDACTED]&camera=[CAMERAID]
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team
  • Before doing anything else, you should test the API in your browser (if it’s a simple GET based API) or using curl from the command line if you need to POST data.

You should see the JSON response:

{
  data: {
    results: [
      {
        camname: "EOS 5D Mark III",
        cammake: "Canon",
        camexifid: "CANON EOS 5D MARK III",
        lensname: "Canon EF 24-105mm f/4 L IS USM",
        author_name: venkat,
        author_url: wikitechy,
        id: "8175491230",
        iso: "1600",
        aperture: "4",
        exposure: "0.00625",
        focal_length: "105",
        small_url: "http://wikitechy.com/8478/8175491230_c94258586b_m.jpg",
        pixels: "22118400",
        lens_id: "920",
        flickr_user_id: "23722023@N06",
        camera_id: "1659",
        big_url: "https://www.wikitechy.com/photos/23722023@N06/8175491230/sizes/o/"
        },
      ...
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team

Minimum viable integration

  • The API works manually - time to code the bare minimum to replicate in Ruby.
  • One of the decisions to make is which library to use to interact with the API.
  • Ruby’s standard library has open-uri built in which provides you with open(url).
  • There doesn’t seem to be a good way to set timeouts using open().
  • The best “solution” is to require 'timeout'and use a Timeout::timeout(seconds) do block
  • Net::HTTP is a great library for interacting over HTTP, but I’m going one step further and using HTTParty, which uses Net::HTTP under the hood. HTTParty provides much of the boilerplate around interacting with an API, is well tested, etc.
  • Other good alternatives include Faraday, which provides low-level controls over the HTTP requests.
  • For our purposes, HTTParty will be just fine.
  • Following the sample code for HTTParty, we end up with a relatively short library:
require 'httparty'

class wikitechy
  include HTTParty
  base_uri 'www.wikitechy.com'

  def api_key
    ENV['PIXELPEEPER_API_KEY']
  end

  def base_path
    "/rest/?method=list_photos&api_key=#{ api_key }"
  end

  def examples_for_camera(camera_id, options = {})
    url = "#{ base_path }&camera=#{ camera_id }"
    self.class.get(url, options)['data']['results']
  end

  def examples_for_lens(lens_id, options = {})
    url = "#{ base_path }&lens=#{ lens_id }"
    self.class.get(url, options)['data']['results']
  end
end
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team
  • It’s a good practice to not include secrets like API keys in source code/source control, so we’re reading the key from an environment variable.
  • If you’re running Heroku, you’ll have to set this via heroku config:set WIKITECHY_API_KEY=$key.
  • For your local server/consoles, you’ll need to export the variable or set it inline when you invoke your command.
  • By including HTTParty in your class, all of your API GET requests are invoked through self.class.get, it will use all the built-in HTTParty tricks, like using the base_uri and automatically decoding the JSON response into a native hash.
  • As a result of leveraging HTTParty, you don’t need much code for a fully-functional API client.

Timeouts

  • A fully-functional, but not a robust API client.
  • If your Rails app calls the API inline with a request (which is common), a 30-second API response will mean your site also takes 30 seconds to respond.
  • If many requests hit the same slow API, it could tie up all your web servers and bring your site down.
  • This used to be common for Facebook apps before they solidified their graph API.
  • The best practice for consuming an API inline with requests is to hard timeout and gracefully degrade.
  • In our case, we need to hard timeout after just one second and return a “fake” empty request, so the template that consumes it can gracefully degrade.
  • HTTParty will raise Net::OpenTimeout if it can’t connect to the server and Net::ReadTimeout if reading the response from the server times out (both in the case that it stalls sending data or is still sending data).
  • So, we simply need to handle both exceptions to return empty hashes, and set the timeout to 1 second.
  • Instead of implementing this timeout-handling logic in both methods, I’m opting for a handle_timeouts function that takes a block.
  • If the block raises an exception, handle_timeouts will catch the exception and return an empty hash.
require 'httparty'

class wikitechy
  include HTTParty
  base_uri 'www.wikitechy.com'
  default_timeout 1 # hard timeout after 1 second

  def api_key
    ENV['wikitechy_API_KEY']
  end

  def base_path
    "/rest/?method=list_photos&api_key=#{ api_key }"
  end

  def handle_timeouts
    begin
      yield
    rescue Net::OpenTimeout, Net::ReadTimeout
      {}
    end
  end

  def examples_for_camera(camera_id, options = {})
    handle_timeouts do
      url = "#{ base_path }&camera=#{ camera_id }"
      self.class.get(url, options)['data']['results']
    end
  end

  def examples_for_lens(lens_id, options = {})
    handle_timeouts do
      url = "#{ base_path }&lens=#{ lens_id }"
      self.class.get(url, options)['data']['results']
    end
  end
end
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team
  • This class now meets my requirements for “production-ready,” since the worst case is adding one second to the request and not showing content, in the case of a timeout.
  • The API server going down or taking a long time to respond can never adversely affect our site more than that.

Caching API responses locally

  • We have protected our site, but one second is still a lot to add to each request - especially if it’s a response we see quite a lot.
  • By implementing a local cache in Redis, we can obviate the need for an external API request and return in mere milliseconds with the cached data.
  • Additionally, your cached responses will still be there if the API server goes down.
  • And in general, it’s being a nice Internet neighbor to not overwhelm the API server with what are essentially duplicate requests.
  • Be careful though; some API servers disallow local caching of responses in their Terms of Service.
  • But unless it’s sensitive user data, most API servers would prefer that you cache.
  • Following the style of handle_timeouts, we’re handling caching via the handle_caching method, which again takes a block (and also an options parameter).
  • Both camera and lense methods have been folded into one examples(options) method, where options is a hash with either a camera_id or lends_id key set.
require 'httparty'

class wikitechy
  include HTTParty
  base_uri 'www.wikitechy.com'
  default_timeout 1 # hard timeout after 1 second

  def api_key
    ENV['wikitechy_API_KEY']
  end

  def base_path
    "/rest/?method=list_photos&api_key=#{ api_key }"
  end

  def handle_timeouts
    begin
      yield
    rescue Net::OpenTimeout, Net::ReadTimeout
      {}
    end
  end

  def cache_key(options)
    if options[:camera_id]
      "wikitechy:camera:#{ options[:camera_id] }"
    elsif options[:lens_id]
      "wikitechy:lens:#{ options[:lens_id] }"
    end
  end

  def handle_caching(options)
    if cached = REDIS.get(cache_key(options))
      JSON[cached]
    else
      yield.tap do |results|
        REDIS.set(cache_key(options), results.to_json)
      end
    end
  end

  def build_url_from_options(options)
    if options[:camera_id]
      "#{ base_path }&camera=#{ options[:camera_id] }"
    elsif options[:lens_id]
      "#{ base_path }&lens=#{ options[:lens_id] }"
    else
      raise ArgumentError, "options must specify camera_id or lens_id"
    end
  end

  def examples(options)
    handle_timeouts do
      handle_caching(options) do
        self.class.get(build_url_from_options(options))['data']['results']
      end
    end
  end
end
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team
  • All of the caching logic is in handle_caching(options) and cache_key(options), the latter of which builds a unique key to store the cached response based on which type of request and for which ID.
  • Redis encourages human-readable key names, but if your key name starts getting unwieldy or unpredictabel in length, it’s perfectly reasonable to compute a hash of your unique identifiers, like SHA1(options.to_json) (pseudocode).
  • handle_caching(options) checks for the existence of a key and returns the payload if available.
  • Otherwise, it yields to the block passed in and stores the result in Redis.
  • Object#tap is a neat method that always returns the Object, but gives you a block with the object as the first named parameter. I
  • t’s a nice pattern for when you finish computing your return value but still need to reference it (to store in a cache, to log, to send an email with, etc).
ruby on rails tutorial tags - ruby , rail , ruby on rails , rail forum , ruby on rails tutorial , ruby tutorial , rails guides , rails tutorial , learn ruby

Consuming the API

  • Using the API client is very straightforward: one line to create an API client instance, and one line to query by camera_id or lens_id.
def example_pictures_for(gear)
  pp = wikitechy.new
  if gear.pp_lens_id.present?
    pp.examples(lens_id: gear.pp_lens_id)
  elsif gear.pp_camera_id.present?
    pp.examples(camera_id: gear.pp_camera_id)
  else
    []
  end.take(8)
end
Clicking "Copy Code" button will copy the code into the clipboard - memory. Please paste(Ctrl+V) it in your destination. The code will get pasted. Happy coding from Wikitechy - ruby on rails tutorial - rails guides - ruby rails - rubyonrails - learn ruby on rails - team

This ruby on rails tutorial page provides you the following key areas such as ruby , rail , ruby on rails , rail forum , ruby on rails tutorial , ruby tutorial , rails guides , rails tutorial , learn ruby , rails form_for , ruby rails , ruby class , what is ruby on rails , rails installer , ruby online , learn ruby on rails , ruby on rails jobs , rails find_by , install rails , easyrail , rubyonrails , link_to rails , ruby on rails developer , learn ruby the hard way , railscasts , ruby on rails examples , ruby on rails vs php , rails 4 , rails activerecord , rails generate , ruby and rails , ruby on rails download , install ruby on rails , ruby net http , what is rails , ruby app , ruby vs ruby on rails , ruby on rails windows , rails for zombies , ruby on rails book , ruby on rails development , ruby on rails ide , ruby on rails tutorial pdf

Related Searches to How to Build a Robust JSON API client with Ruby