redux-fetch-api

1.0.2 • Public • Published

version license installs build mind BLOWN

redux-fetch-api v1.0.2

Isomorphic fetch api for use with redux-apis

Installation

npm install --save redux-fetch-api

NOTE When using redux-fetch-api on older versions of Node, or older browsers that don't support Promises, make sure to install a Promise polyfill as well. This library is tested on Node JS 0.10, as can be seen in the .travis.yml config file, using babel-polyfill.

Dependencies and imports

redux-fetch-api does not depend on, but is designed to work well with redux-apis.

import { remote, endpoint, fetcher } from 'redux-fetch-api';

Or, using ES5 / require:

var remote = require('redux-fetch-api').remote;
var endpoint = require('redux-fetch-api').endpoint;
var fetcher = require('redux-fetch-api').fetcher;

NOTE: redux-fetch-api depends on a global function fetch being available, as specified by the whatwg fetch specification.

For NodeJS or older browsers, use a polyfill such as github/fetch, node-fetch or isomorphic-fetch. Alternatively, you can explicitly provide a fetch implementation using the @fetcher decorator function.

Usage

Use @remote to decorate Apis with a scoped isomorphic fetch method. Set server-side and client-side base-urls for fetching with @endpoint. Override the default fetcher (that uses global fetch) with @fetcher.

@remote(url='')

Decorate an Api class with @remote to give it it's own scoped version of fetch, just like it has scoped versions of getState and createAction.

@remote('http://example.com/api')
class App extends Api {
  someFunction() {
    return this.fetch('/products/Wedding+Dresses/')
      .then(() => {
        // here, fetch has completed successfully
      })
      .catch((error) => {
        // Something went wrong
      });
  }
}

Or, without relying on the decorators proposal:

const App = remote('http://example.com/api')(
  class App extends Api {
    someFunction() {
      return this.fetch('/products/Wedding+Dresses/')
        .then(() => {
          // here, fetch has completed successfully
        })
        .catch((error) => {
          // Something went wrong
        });
    }
  }
)

In both cases, instances of App will get a fetch method that is scoped to the URLs that were assigned to it by @remote and it's parent Apis. This allows us to postpone decicions about those URLs to later/higher up the Api hierarchy.

We can use @remote on classes as well as instances. This allows us to only declare that we will be using .fetch, but postponing the assignment of the url to later:

@remote
class MyApi extends Api {
  doIt() {
    return this.fetch('/something');
  }
}
 
// ...later
 
const myApp = remote('http://example.com')(
  new MyApi()
)
 
myApp.doIt(); // fetches 'http://example.com/something'

.fetch(url='', opts=undefined, abs=false)

Any apis decorated with @remote will get a .fetch method that performs fetch calls scoped to the current api. When called, it will prefix the given url with the url set by @remote. It then traverse the api hierarchy upwards, looking for fetch methods. If it finds one, it delegates to the found method. This allows us to map our entire Api hierarchy onto urls very easily:

@remote
class Module extends Api {
  doIt() {
    return this.fetch('/something');
  }
}
 
@remote
class ComplexModule extends Api {
  constructor(state) {
    super(state);
    this.submodule1 = remote('/sub1')(link(this, new Module()));
    this.submodule2 = remote('/sub2')(link(this, new Module()));
  }
}
 
@remote('http://example.com')
class App extends Api {
  constructor(state) {
    super(state);
    this.moduleA = remote('/modA')(link(this, new Module()));
    this.moduleB = remote('/modB')(link(this, new Module()));
    this.moduleC = remote('/modC')(link(this, new ComplexModule()));
  }
}
 
const app = new App().init();
app.moduleA.doIt(); // fetches 'http://example.com/modA/something'
app.moduleB.doIt(); // fetches 'http://example.com/modB/something'
app.moduleC.submodule1.doIt(); // fetches 'http://example.com/modC/sub1/something'
app.moduleC.submodule2.doIt(); // fetches 'http://example.com/modC/sub2/something'

@endpoint(url='', altUrl=null, usAlt=runningInBrowser())

Allows you to intervene in the process by which .fetch traverses the parent hierarchy. If it encounters an api with a fetch method and that api is decorated with @endpoint, it will stop traversing at that api and no longer look at the parent of that api.

This allows us to map our api hierarchy to multiple endpoints:

@remote
class Search extends Api {}
 
@remote('http://example.com/api')
class App extends Api {
  constructor(state){
    super(state);
 
    this.products = endpoint('http://products.example.com')(
      link(this, new Search())
    )
    this.people = remote('/people')(
      link(this, new Search())
    )
  }
}
app.products.fetch();  // fetches 'http://products.example.com'
app.people.fetch();    // fetches 'http://example.com/api/people'

The parameter altUrl allows us to use different urls in different environments. When specified, it will be used if parameter useAlt is truthy. useAlt is initialized to the result of runningInBrowser() by default, meaning altUrl will be used when running in a browser environment. You can easily use your own test conditions here.

@fetcher(fetchMethod)

This decoration allows you to override the default fetch method on any api you decorate with it. Whether the overridden fetch method is actually used depends on the position of the api in the hierarchy (due to .fetch traversing it's parents). As such you should set this on the top-level api and any apis decorated with @endpoint.

This library's test scripts make heavy use of @fetcher to replace the default fetch method from isomorphic-fetch with a stub that only tracks which urls have been fetched.

See also

If you are working with fetch, then you are doing async work. If you inherit from Async instead of from Api, you get async state management for free.

Feedback, suggestions, questions, bugs

Please visit the issue tracker for any of the above. Don't be afraid about being off-topic. Constructive feedback most appreciated!

Copyright

© 2016, Stijn de Witt. Some rights reserved.

License

Creative Commons Attribution 4.0 (CC-BY-4.0)

Package Sidebar

Install

npm i redux-fetch-api

Weekly Downloads

1

Version

1.0.2

License

CC-BY-4.0

Last publish

Collaborators

  • stijndewitt