docs: function createFetcher(url, opt, _name) {
      const headers = {
        'Content-Type': 'application/json',
        'User-Agent': 'thorin/' + thorinVersion
      };
      if (typeof opt.authorization === 'string') {
        let authToken = opt.authorization;
        delete opt.authorization;
        headers['Authorization'] = 'Bearer ' + authToken;
      }
      opt = thorin.util.extend({
        method: 'POST',
        follow: 1,
        timeout: 40000,
        headers: headers
      }, opt);
  
      function fetcher(action, _payload) {
        return fetcher.dispatch.apply(this, arguments);
      }
  
      /*
       * Handles the fetcherObj.dispatch(actionName, _payload)
       * NOTE:
       *  giving a "request" callback in the options will
       *  call when the http request object is available.
       * */
      function doDispatch(action, _payload, _options) {
        let bodyPayload = {
          type: action,
          payload: {}
        };
        if (typeof _payload === 'object' && _payload) {
          bodyPayload.payload = _payload;
        }
        let options = (typeof _options === 'object' && _options ? _options : {});
        try {
          bodyPayload = JSON.stringify(bodyPayload);
        } catch (e) {
          return Promise.reject(thorin.error('FETCH.DATA', 'Failed to stringify fetch payload.', e, 400));
        }
        let fetchOptions = thorin.util.extend({
          body: bodyPayload,
          headers: {
            connection: 'keep-alive'
          }
        }, opt, options);
        let statusCode;
        return nodeFetch(url, fetchOptions)
          .then((res) => {
            statusCode = res.status;
            return res.json();
          }).then((resultData) => {
            if (statusCode >= 200 && statusCode <= 299) {
              if (resultData.type) {
                delete resultData.type;
              }
              return Promise.resolve(resultData);
            }
            const errData = resultData.error || {},
              msg = errData.message || 'Failed to execute fetch',
              status = errData.status || 400,
              code = (errData.code || 'FETCH.ERROR');
            let err = thorin.error(code, msg, status);
            err.ns = 'FETCH';
            if (!err.data) err.data = {};
            err.data.action = action;
            throw err;
          }).catch((e) => {
            if (e && e.ns === 'FETCH') return Promise.reject(e);
            let msg = '',
              status = 400,
              code = 'FETCH.';
            if (e) {
              if (e instanceof SyntaxError) {
                code += 'RESPONSE';
                msg = 'Request data could not be processed.';
              } else {
                switch (e.type) {
                  case 'request-timeout':
                    code += 'TIMEOUT';
                    msg = 'Request timed out';
                    break;
                  default:
                    code += 'ERROR';
                    msg = 'Could not contact the server';
                    status = statusCode || 400;
                }
              }
            }
            let tErr = thorin.error(code, msg, status, e);
            if (!tErr.data) tErr.data = {};
            tErr.data.action = action;
            return Promise.reject(tErr);
          });
      }
  
      fetcher.dispatch = function dispatch(action, _payload) {
        let args = Array.prototype.slice.call(arguments);
        if (typeof args[args.length - 1] === 'function') {
          let fn = args.pop();
          return doDispatch
            .apply(this, args)
            .then((r) => {
              fn(null, r);
            })
            .catch((e) => {
              fn(e, null);
            });
        }
        return doDispatch.apply(this, arguments);
      };
  
      if (_name) {
        /* Destroys the fetcher. */
        fetcher.destroy = function DestroyFetcher() {
          delete fetchers[_name];
        }
      }
  
      return fetcher;
    }