David J.
David J.

Reputation: 32715

API Design that Caches an External API

I have built a lightweight Ruby code to wrap an external API:

api = ExternalAPI.new
api.expensive_operation(object)

It works fine. However, since the API is expensive, I don't want to call it unless necessary. This is why I am creating a higher-level API that wraps the API call with local caching. I don't want the application to have to worry about the details about how the API is cached. (Caching could be accomplished by memory, disk, abaci, even pigeons -- it is not the application's concern.)

Here is what I am currently considering:

wrapper = ExternalAPIWrapper.new
wrapper.expensive_operation(object)

I don't like about the name ExternalAPIWrapper. It is generic, does not convey the purpose of the wrapper. In particular, it does not indicate that it is checks a local cache first and only hits the low-level API if necessary.

I'm looking for answers that improve upon this starting point. Here are some things I'm looking for:

  1. A better name for the high-level class
  2. A better style API
  3. Design pattern(s) that might help
  4. (Perhaps a longshot...) a Ruby gem that wraps and caches API calls

Upvotes: 3

Views: 1088

Answers (3)

David J.
David J.

Reputation: 32715

Rob, a friend of mine, found APICache:

APICache (aka api_cache)

APICache allows any API client library to be easily wrapped with a robust caching layer. It supports caching (obviously), serving stale data and limits on the number of API calls. It's also got a handy syntax if all you want to do is cache a bothersome url.

APICache supports multiple caching strategies, including:

  • A simple in-memory cache
  • Various backends via Moneta, a "unified interface to key-value stores"
  • Memcached via Dalli, named after Dalí's Persistence of Memory

Upvotes: 2

Mr Morphe
Mr Morphe

Reputation: 694

Too little detail to know exactly what you want but I thought of a bridge pattern straight away since you are essentially trying to decouple your abstraction and implementation when looking at varying your cache backend.

Upvotes: 0

fresskoma
fresskoma

Reputation: 25781

For #1 the name that comes to mind is CachedExternalAPI:) Not sure what you mean by "a better style API", though.

As for #3/4: I don't know of a RubyGem that does this sort of caching thing, but I'd implement the cached API in a "metaprogramming" fashion, automatically generating the methods for the cached API calls, e.g.

class CachedExternalAPI
  @cache = { }

  class << self

    [:foo, :bar, :baz].each do |m|
      define_method m do
        return (puts "Totally cached!"; @cache[m]) if not @cache[m].nil?
        puts "Not cached :("
        @cache[m] = 42
      end
    end

  end
end

CachedExternalAPI.foo()
CachedExternalAPI.foo()

CachedExternalAPI.bar()
CachedExternalAPI.bar()

Which will yield

Not cached :(
Totally cached!
Not cached :(
Totally cached!

This of course assumes that the caching mechanism for all API calls is the same. But if your API is cachable that way, you can keep the cached API wrapper pretty DRY.

Upvotes: 3

Related Questions