graphql / dataloader

DataLoader is a generic utility to be used as part of your application's data fetching layer to provide a consistent API over various backends and reduce requests to those backends via batching and caching.
MIT License
12.89k stars 515 forks source link

Custom cache examples #39

Closed tonyxiao closed 8 years ago

tonyxiao commented 8 years ago

Are there examples of custom cache implementations with dataloader? For example using redis to provide a LRU cache with expiry? Or in-memory cache with upper memory bound.

lukaswelte commented 8 years ago

The tests offer an example usage:

  describe('Accepts custom cacheMap instance', () => {

    class SimpleMap {
      stash: Object;

      constructor() {
        this.stash = {};
      }
      get(key) {
        return this.stash[key];
      }
      set(key, value) {
        this.stash[key] = value;
      }
      delete(key) {
        delete this.stash[key];
      }
      clear() {
        this.stash = {};
      }
    }

    it('Accepts a custom cache map implementation', async () => {
      var aCustomMap = new SimpleMap();
      var identityLoadCalls = [];
      var identityLoader = new DataLoader(keys => {
        identityLoadCalls.push(keys);
        return Promise.resolve(keys);
      }, { cacheMap: aCustomMap });

      // Fetches as expected

      var [ valueA, valueB1 ] = await Promise.all([
        identityLoader.load('a'),
        identityLoader.load('b'),
      ]);

      expect(valueA).to.equal('a');
      expect(valueB1).to.equal('b');

      expect(identityLoadCalls).to.deep.equal([ [ 'a', 'b' ] ]);
      expect(Object.keys(aCustomMap.stash)).to.deep.equal([ 'a', 'b' ]);

      var [ valueC, valueB2 ] = await Promise.all([
        identityLoader.load('c'),
        identityLoader.load('b'),
      ]);

      expect(valueC).to.equal('c');
      expect(valueB2).to.equal('b');

      expect(identityLoadCalls).to.deep.equal([ [ 'a', 'b' ], [ 'c' ] ]);
      expect(Object.keys(aCustomMap.stash)).to.deep.equal([ 'a', 'b', 'c' ]);

      // Supports clear

      identityLoader.clear('b');
      var valueB3 = await identityLoader.load('b');

      expect(valueB3).to.equal('b');
      expect(identityLoadCalls).to.deep.equal(
        [ [ 'a', 'b' ], [ 'c' ], [ 'b' ] ]
      );
      expect(Object.keys(aCustomMap.stash)).to.deep.equal([ 'a', 'c', 'b' ]);

      // Supports clear all

      identityLoader.clearAll();

      expect(Object.keys(aCustomMap.stash)).to.deep.equal([]);
    });

  });

});

The example in it is mainly:

  describe('Accepts custom cacheMap instance', () => {

    class SimpleMap {
      stash: Object;

      constructor() {
        this.stash = {};
      }
      get(key) {
        return this.stash[key];
      }
      set(key, value) {
        this.stash[key] = value;
      }
      delete(key) {
        delete this.stash[key];
      }
      clear() {
        this.stash = {};
      }
    }

    it('Accepts a custom cache map implementation', async () => {
      var aCustomMap = new SimpleMap();
      var identityLoadCalls = [];
      var identityLoader = new DataLoader(keys => {
        identityLoadCalls.push(keys);
        return Promise.resolve(keys);
      }, { cacheMap: aCustomMap });

So creating a class that implements get set delete and clear is sufficient that you can pass it into the dataloader as cacheMap. So you can create e.g. a class that uses redis in these methods to store or retrieve the value and you are good to go.

const dataloader = new DataLoader(keys => {
 // the loader code
}, {cacheMap: myRedisObject});
tonyxiao commented 8 years ago

Got it, thanks for the reference. What about async cache sources, powered by something like Redis? Is that supported by data loader?

lukaswelte commented 8 years ago

Looking at the source code I'd say yes, but maybe just do a little test.

tonyxiao commented 8 years ago

Got it, thanks for the hint. Will close this for now then :)

B-3PO commented 7 years ago

Here is a implementation that adds a Redis cache layer in place of the in memory cache. https://www.npmjs.com/package/redis-cache-dataloader