Giter Site home page Giter Site logo

redux-api-middleware's People

Contributors

agraboso avatar artursvonda avatar audionerd avatar b0gok avatar bareynol avatar barrystaes avatar brian-growratio avatar bs1180 avatar darthrellimnad avatar dependabot[bot] avatar einomi avatar foaly-nr1 avatar georgebonnr avatar graham42 avatar iamandrewluca avatar jtribble avatar just-paja avatar justindsmith avatar lizell avatar lraivio avatar michael-freidgeim-webjet avatar mikelaurence avatar mohanzhang avatar nason avatar renefs avatar server-monitor avatar williammer avatar ztanner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-api-middleware's Issues

API overhaul (comments welcome)

For a while now I've been thinking about a relatively major overhaul of redux-api-middleware. Time and again, people have expressed their need for extracting different kinds of data from server responses, and processing them differently (see #4, #9, #10, #13). The original source of the code in this module, the real-world example in the redux repository, was probably though by Dan Abramov in the restrictive setting of exactly what he wanted to do in that example, and not in terms of how it could accommodate the needs of a large cross-section of the developers using redux.

I've just pushed a new branch to redux-api-middleware named next. As of now, the only new thing is the README. I've spent some time trying to come up with a schema that is not only flexible (allowing for the kind of access to server responses that people have asked for, and might ask for in the future), but also robust (in the sense of being able to deal with errors precisely and predictably), and that README is what I've come up with.

I haven't started writing code implementing the changes outlined there — I hope to start doing so in the next few days. For the moment, I'd love to get input from those of you that have been using (or playing with) redux-api-middleware, to see if this rethinking of the API suits your needs.

It's late already today, but I'll try to write another post tomorrow detailing some of the most important changes and why I went for them. Just giving everybody a heads up in the meantime — after all, I put quite a lot of effort into the README itself.

cc @svnlto @seokgyo @lraivio @mohanzhang @eventhough @RanzQ @vdemin @latentflip

The release of 1.0.0-beta3 may not be built correctly

The release of 1.0.0-beta3 from npmjs.org may be wrong built. It doesn't support credentials option at all, since that key was considered illegal.

screen shot 2015-11-04 at 20 07 31

I cloned the repo & built it myself. Everything works fine now.

Applying entities to state?

Probably a stupid question, but I was wondering how, or if, you're supposed to apply an entity to state after it's been retrieved.

'Unhandled promise rejection' on Firefox

Been using this library (v1.0.0-beta3) in Chrome on OS X and it works great, but I'm having issues trying to use it in Firefox. I'm building using the babel-es2015 preset, with Browserify.

On Firefox (43.0.4, latest stable release), I get the following error:

Unhandled promise rejection TypeError: action.payload is undefined

The success (or failure) action is never dispatched. I'm seeing this happening on Chrome for Android too. Safari works OK.

I'm not sure if this is because I'm not using a required polyfill for Promises or similar - the Babel documentation is a little unclear as to whether the ES2015 preset includes promises.

isRSAA returning false - EntitySchema vs Schema?

Thanks for this timely package - I finally got to the point a couple of days ago where I understood the "real world" redux example and wondered if anyone generalized the middleware - turns out you did!

I'm having a hard time getting a trivial action to be recognized as isRSAA. In the Chrome developer console, when I inspect the reason the function is returning false, it turns out that

> schema instanceof _normalizr.Schema
false

because schema in this context is actually a EntitySchema. I noticed that a new version of normalizr came out just a day ago, so I tried both version 1.0.0 and 0.1.3 but am having the same problem. Any ideas what I'm missing?

3xx status appear as failure?

After browsing through the repo, unless I'm mistaken, it appears as though any server response above 299 will be treated as an error. What about redirects ie. (responses with status codes in the 3xx range)?

It think it's a bit awkward to treat a simple redirect as an error, though some clients might want to do that, I can't see all of them wanting to do that.

[CALL_API].credentials is not supported

In the documentation, it talks about [CALL_API].credentials being an acceptable option but when I try and set it to 'same-origin', it fails on the isRSAA check with an error... Error: Actions may not have an undefined "type" property. Have you misspelled a constant?

Fails on the isRSAA check.

function apiMiddleware({ getState }) {
  return (next) => (action) => {
    const callAPI = action[CALL_API];
    if (!isRSAA(action)) {
      return next(action);
    }

Here is my action...

import {CALL_API} from 'redux-api-middleware';

const getUsers = (data) => {
  return {
    [CALL_API]: {
      endpoint: '/api/users',
      method: 'GET',
      credentials: 'same-origin',
      types: ['REQUEST', 'SUCCESS', 'FAILURE']
    }
  };
};

export default {
  getUsers
};

Looking through your code I can see no mention of the credentials property. I also see no mention in your tests. Is it because your documentation is wrong or am I doing something wrong?

What to do when response is not JSON?

I have a DELETE endpoint that returns 204 No Content as suggested here. Currently, the middleware expects all responses to be JSON, but should this requirement be relaxed?

How to send GET parameters?

Hi, I'm trying to send some parameters with GET request, but have no idea how to do it.
Btw, when you initialize body parameter with method GET fetch requests stop working without any errors in console.

endpoint: <some_url>
method: 'GET',
body: JSON.stringify({userId: userId}),

Transforming response data before firing the success action

Would it be possible to add a way to transform the response data before passing it to the success action?
The use case I have in mind would be to camelize all the data keys before the data reaches reducers.
Like in https://github.com/rackt/redux/blob/master/examples/real-world/middleware/api.js#L35.

If [CALL_API] had a transform property (function to transform the response data) or something similar it would work on other use cases as well.

Middleware to time and log delays between request and reponse (with code)

I wrote a middleware so that we can easily time delays between request and success/failure. I thought I could share if anyone is interested. It does not work if a second similar request is sent before a response to the first one is received. If you know how to deal with this, let me know.

Here is the output:

Dispatching A_REQUEST
Received A_SUCCESS after 500ms

Here is how to activate it for a single request via meta in a RSA action:
meta: { timer: [A_SUCCESS, A_FAILURE] } or meta: { timer: A_SUCCESS }

And here is the middleware:

// Holds a timestamp for each expected action
const timers = {}

const apiTimeLoggerMiddleware = (store) => (next) => (action) => {
  if (action.meta && action.meta.timer) {
    const expected = action.meta.timer
    if (typeof expected === 'string' || expected instanceof String) {
      timers[expected] = new Date().getTime()
      console.log(`Dispatching ${action.type}`)
    } else if (Array.isArray(expected)) {
      const timestamp = new Date().getTime()
      for (const item of expected) { timers[item] = timestamp }
      console.log(`Dispatching ${action.type}`)
    } else {
      console.error(`${action.type} has a \'timer\' meta with unexpected type of ${typeof expected}.`)
    }
  }

  if (action.type in timers) {
    console.log(`Received ${action.type} after ${new Date().getTime() - timers[action.type]}ms`)
  }

  return next(action)
}

export default apiTimeLoggerMiddleware

Two consecutive `request` FSAs dispatched on network error

Contrary to the documentation, the current implementation dispatches two consecutive request FSAs when a network error occurs.

In README.md it says in section "Lifecycle", bullet point no. 3:

Now that redux-api-middleware is sure it has received a valid RSAA, it will try making the API call. If everything is alright, a request FSA will be dispatched with the following property: (…)
(…)
If such an error occurs, a different request FSA will be dispatched (instead of the one described above). It will contain the following properties: (…)

The important thing to note here is "a different request FSA (…) instead of the one described above".

However, the actual code in src/middleware.js sends two request FSAs in case of exceptions thrown during the fetch call:

// We can now dispatch the request FSA
next(await actionWith(
  requestType,
  // (…)
));

try {
  // Make the API call
  var res = await fetch(endpoint, { method, body, credentials, headers });
} catch(e) {
  // The request was malformed, or there was a network error
  return next(await actionWith(
    {
      ...requestType,
      // (…)
    },
    // (…)
  ));
}

This is problematic as we now cannot be sure which of the two request FSAs is which, or rather: once the non-error request FSA has fired, we should be able to assume that the request is either handled successfully, i.e. a success FSA shows up, or fails with a failure FSA. Receiving another request FSA, now with error set, is counter-intuitive (and doesn't match the documentation).

However, I'm not really sure how best to proceed from here. Since the fetch call can fail for all kinds of reasons, even late in the request (server accepted but then closed connection unexpectedly), it seems impossible to simply delay the first, non-error request FSA until we can be sure that no error will be thrown.

Instead, we should probably always send a failure FSA, i.e. both when the server responds with a status code other than 2xx and when a network error occurs. In this case, it would be sufficient to change the documentation and simply replace requestType in the code above with failureType.

Incidentally, this seems to also match the documentation regarding Failure Type Descriptors where the code sample shows the following if-check:

meta: (action, state, res) => {
  if (res) {
    return {
      status: res.status
      statusText: res.statusText
    };
  } else {
    return {
      status: 'Network request failed'
    }
  }
}

In the current implementation, the res argument to meta is always set. With the proposed change, it would either be set (server responded with code other than 2xx) or unset (network error occurred).

In case this issue is accepted, please let me know if I should prepare a pull request for the proposed change (documentation and code).

Accessing action.endpoint from meta and action in RSAA descriptors

Hello,

I am trying to display the action.endpoint in meta or payload, but it ends up showing undefined in dev tools. Everything else is working, the actions are normally dispatched and the result from the API is also ok

[CALL_API]: {
    endpoint: 'http://example.com/api',
    method: 'GET',
    types: [
      {
        type: 'REQUEST',
        meta: (action, state) => ({ endpoint: action.endpoint })
      },
      {
        type: 'SUCCESS',
        payload: async function (action, state, res) {
          const contentType = res.headers.get('Content-Type')
          // Just making sure res.json() does not raise an error
          if (contentType && ~contentType.indexOf('json')) {
            return await res.json()
          }
        }
      },
      'FAILURE'
    ]
  }

Same goes with payload instead of meta in the RSAA descriptor. Is there something missing ?

Thank you !

Header middleware

I created a middleware which adds an authentication token but for some reason the header key gets converted to lowercase. Not actually a problem but I just wonder why. Does this issue originate from isomorphic-fetch or what?

My middleware:

import { CALL_API } from 'redux-api-middleware'

const authMiddleware = ({getState, dispatch}) => next => action => {

  // Add header
  if (action[CALL_API]) {
    action[CALL_API].headers = {
      Authorization: 'Bearer ' + getState().token,
      ...action[CALL_API].headers
    }
  }

  return next(action)

}

export default authMiddleware

I tried with Authorization, 'Authorization' and ['Authorization']

response headers

I'm looking for a way to get ahold of the response headers from a POST request. How would I go about this?

What is the reasoning for a Request FSA with error flag? (vs a Failure FSA)

As i understand now - when a REQUEST FSA gets fired, this can result in a:

  • REQUEST FSA with error flag (for example when the API request cant be made)
  • SUCCESS FSA (for example for API response 200 OK)
  • FAILURE FSA (for example for API response 404 Not Found)

But in my view one REQUEST FSA should result in (only and) exactly one FSA, either SUCCESS or FAILURE.

I haven't actually used it, but i dont really understand the first one. Whats the argument for not returning a FAILURE FSA in that case?

Undefined action "type" property when bundling with webpack

When I bundle my app using webpack, for production, with this plugin defined:

new webpack.DefinePlugin({ 'process.env': {NODE_ENV: JSON.stringify('production')} })

I get the following error:

Actions may not have an undefined "type" property. Have you misspelled a constant?

Everything works great when I'm running in development mode. Anyone else run into this issue or have an idea what I may be doing wrong? Thanks for any suggestions...

Object instead of Array

Hello,

I use this middlware to fetch data from my API. However, if my API returns an array, I get an Object in the payload, instead of an Array.

For exemple, my API call GET /api/message returns

[
  { "id": 1, "content": "first message" },
  { "id": 2, "content": "second message"},
  ...,

But in my reducer, in case of sucess, action.payload is an Object.
How can I do to get an Array instead ?

AMD loading?

Would really like to try this out in my project, but AMD loading (define([deps], factory)) is a requirement of my environment. Any advice for getting this to load in an AMD environment with little effort?

not working when browser not support Symbol

if browser not support Symbol ,the action.key is string not equal to object , so push error and not send api

for (let key in action) {
    if (key !== [CALL_API]) {
      validationErrors.push(`Invalid root key: ${key}`);
    }
  }

I fount if browser support Symbol , do not enter this for loop . because action.key is real Symbol

Maybe this validate is not necessary .

Error when trying to customize FSA

Works:

{
    [CALL_API] : {
      endpoint : 'some-endpoint',
      method   : 'POST',
      types    : [
        'REQUEST',
        'SUCCESS',
        'FAILURE'
      ],
      body : body
    }
}

Blows up:

{
    [CALL_API] : {
      endpoint : 'some-endpoint',
      method   : 'POST',
      types    : [
        { type : 'REQUEST', payload : body },
        'SUCCESS',
        'FAILURE'
      ],
      body : body
    }
}
Uncaught Error: Invariant Violation: Objects are not valid as a React child (found: object with keys {type, payload}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `LogMonitorAction`.invariant @ invariant.js:39traverseAllChildrenImpl @ traverseAllChildren.js:158traverseAllChildren @ traverseAllChildren.js:186ReactChildReconciler.instantiateChildren @ ReactChildReconciler.js:52ReactMultiChild.Mixin._reconcilerInstantiateChildren @ ReactMultiChild.js:197ReactMultiChild.Mixin.mountChildren @ ReactMultiChild.js:232ReactDOMComponent.Mixin._createContentMarkup @ ReactDOMComponent.js:591ReactDOMComponent.Mixin.mountComponent @ ReactDOMComponent.js:479ReactReconciler.mountComponent @ ReactReconciler.js:37ReactMultiChild.Mixin.mountChildren @ ReactMultiChild.js:241ReactDOMComponent.Mixin._createContentMarkup @ ReactDOMComponent.js:591ReactDOMComponent.Mixin.mountComponent @ ReactDOMComponent.js:479ReactReconciler.mountComponent @ ReactReconciler.js:37ReactCompositeComponentMixin.mountComponent @ ReactCompositeComponent.js:225wrapper @ ReactPerf.js:66ReactReconciler.mountComponent @ ReactReconciler.js:37ReactMultiChild.Mixin.mountChildren @ ReactMultiChild.js:241ReactDOMComponent.Mixin._createContentMarkup @ ReactDOMComponent.js:591ReactDOMComponent.Mixin.mountComponent @ ReactDOMComponent.js:479ReactReconciler.mountComponent @ ReactReconciler.js:37ReactCompositeComponentMixin.mountComponent @ ReactCompositeComponent.js:225wrapper @ ReactPerf.js:66ReactReconciler.mountComponent @ ReactReconciler.js:37ReactMultiChild.Mixin._mountChildByNameAtIndex @ ReactMultiChild.js:474ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:378ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.performUpdateIfNecessary @ ReactCompositeComponent.js:421ReactReconciler.performUpdateIfNecessary @ ReactReconciler.js:102runBatchedUpdates @ ReactUpdates.js:129Mixin.perform @ Transaction.js:136Mixin.perform @ Transaction.js:136assign.perform @ ReactUpdates.js:86flushBatchedUpdates @ ReactUpdates.js:147wrapper @ ReactPerf.js:66Mixin.closeAll @ Transaction.js:202Mixin.perform @ Transaction.js:149ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62batchedUpdates @ ReactUpdates.js:94ReactEventListener.dispatchEvent @ ReactEventListener.js:204
ReactCompositeComponent.js:457 Uncaught TypeError: Cannot read property 'componentWillReceiveProps' of nullReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:457wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.performUpdateIfNecessary @ ReactCompositeComponent.js:421ReactReconciler.performUpdateIfNecessary @ ReactReconciler.js:102runBatchedUpdates @ ReactUpdates.js:129Mixin.perform @ Transaction.js:136Mixin.perform @ Transaction.js:136assign.perform @ ReactUpdates.js:86flushBatchedUpdates @ ReactUpdates.js:147wrapper @ ReactPerf.js:66Mixin.closeAll @ Transaction.js:202Mixin.perform @ Transaction.js:149ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62enqueueUpdate @ ReactUpdates.js:176enqueueUpdate @ ReactUpdateQueue.js:24ReactUpdateQueue.enqueueSetState @ ReactUpdateQueue.js:190ReactComponent.setState @ ReactComponent.js:65handleChange @ connect.js:215(anonymous function) @ createStore.js:132dispatch @ createStore.js:131updateScrollTop @ LogMonitor.js:144
ReactCompositeComponent.js:457 Uncaught (in promise) TypeError: Cannot read property 'componentWillReceiveProps' of null(…)ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:457wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactChildReconciler.updateChildren @ ReactChildReconciler.js:84ReactMultiChild.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:216ReactMultiChild.Mixin._updateChildren @ ReactMultiChild.js:351ReactMultiChild.Mixin.updateChildren @ ReactMultiChild.js:326ReactDOMComponent.Mixin._updateDOMChildren @ ReactDOMComponent.js:871ReactDOMComponent.Mixin.updateComponent @ ReactDOMComponent.js:700ReactDOMComponent.Mixin.receiveComponent @ ReactDOMComponent.js:645ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.receiveComponent @ ReactCompositeComponent.js:405ReactReconciler.receiveComponent @ ReactReconciler.js:87ReactCompositeComponentMixin._updateRenderedComponent @ ReactCompositeComponent.js:562ReactCompositeComponentMixin._performComponentUpdate @ ReactCompositeComponent.js:544ReactCompositeComponentMixin.updateComponent @ ReactCompositeComponent.js:473wrapper @ ReactPerf.js:66ReactCompositeComponentMixin.performUpdateIfNecessary @ ReactCompositeComponent.js:421ReactReconciler.performUpdateIfNecessary @ ReactReconciler.js:102runBatchedUpdates @ ReactUpdates.js:129Mixin.perform @ Transaction.js:136Mixin.perform @ Transaction.js:136assign.perform @ ReactUpdates.js:86flushBatchedUpdates @ ReactUpdates.js:147wrapper @ ReactPerf.js:66Mixin.closeAll @ Transaction.js:202Mixin.perform @ Transaction.js:149ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62enqueueUpdate @ ReactUpdates.js:176enqueueUpdate @ ReactUpdateQueue.js:24ReactUpdateQueue.enqueueSetState @ ReactUpdateQueue.js:190ReactComponent.setState @ ReactComponent.js:65handleChange @ connect.js:215(anonymous function) @ createStore.js:132dispatch @ createStore.js:131dispatch @ instrument.js:323(anonymous function) @ historyMiddleware.js:24(anonymous function) @ replaceRoutesMiddleware.js:15(anonymous function) @ index.js:12(anonymous function) @ apiMiddleware.js:49(anonymous function) @ bindActionCreators.js:14(anonymous function) @ handleSubmit.js:36
ReactCompositeComponent.js:457 Uncaught TypeError: Cannot read property 'componentWillReceiveProps' of null

JSON.stringify missing for body?

I am getting the impression I am the first person other than you to use this package :) This is good because now we are in the same boat ;)

I was having some trouble POSTing and PUTing, and I found the cause to be the request body not being stringified. The example on the fetch readme (find "Post JSON") shows this:

fetch('/users', {
  method: 'post',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})

Perhaps wrap JSON.stringify(body) on this line? https://github.com/agraboso/redux-api-middleware/blob/master/src/index.js#L41

Bailout option Should Throw Error to Reducer

Use Case:
Using Auth middleware to check if user logged in (via a Token). In the case they are not logged in (there is no Token or it is expired), should be able to throw an error and get out before making the request that you already know will fail.

Maybe this defeats purpose of bailout, but think I am going to fork and implement..

redux-thunk, return promise

Is there a way to use this middleware along with redux-thunk so we can do promise chaining after the fetch completes? For example, some react form libraries require validation errors to be returned after form submission.

Array.includes does not work

So I am using babel@^5.8.23 to compile to codebase, when I am using redux-api-middleware, the browser will throw "Uncaught TypeError: validRootKeys.includes is not a function".

Also for .babelrc mine is :

{
  "stage": 0,
  "loose": "all"
}

Not handling Failure FSAs (?)

Most likely I'm misinterpreting the docs, but here's my problem:

I would like to handle failed api requests in a similar way to how I handle the successful api requests. Yet as far as I can tell, the SEARCH_FAILURE FSA never gets handled, though it is treated (AFAICT) identically to the SEARCH_SUCCESS FSA. It does seem to be created and dispatched, based on what I see in the devtools.

I have this

import { CALL_API } from 'redux-api-middleware'
import { handleActions } from  'redux-actions'
const searchReducer = handleActions({

 SEARCH_SUCCESS: (state = defaultState, action) => {
     return {
   ...state,
    search_results: ({...action.payload}),
    api: {
        requestPending: false,
        searchPending: false
    },
  }
 },

SEARCH_FAILURE:  function(state = defaultState, action) {
    console.log("Handling SEARCH_FAILURE given state, action: ", state, action)

    return {
            ...state,
            search_results: {Total: 0},
            api: {
                requestPending: false,
                error: action.payload
                },
            errors: [action.payload, ...state.errors]
        }
},
})

the SEARCH_SUCCESS FSA gets handled by searchReducer, but when the server gives a 400 response, the SEARCH_FAILURE handler never gets called--at least I don't see the log output I would expect, and the state sure doesn't end up looking right. I do see a SEARCH_FAILURE entry in the redux devtools panel, however.

Serving to confuse me further, here is the declaration I have at the moment for creating the RSAA

export function doSearch( selected_filters, page ){
let qs = SearchPage.constructQueryString(selected_filters, page)

return {
  [CALL_API]: {
  endpoint: `/api/songs/search?${qs}`,
  method: 'GET', 
  types: [
        {type: SEARCH_REQUEST},
        {type: SEARCH_SUCCESS},
        {
           type: SEARCH_FAILURE,
           payload: (action, state, res) => {
                 if (400 === res.status)
                  {
                     console.log(`${SEARCH_FAILURE} payload: `, action, state, res)
                  }
                 return res
              }
            },
         ],
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include'
     }
  }
}

the payload function is being called and logging more or less what I'd expect. So what am I messing up here? As far as I can discern from multiple readings of the docs for redux-api-middleware, this setup should yield the behavior I want, but it does not. The successes succeed, but the failures fail...

And I'm posting here because it seems like this is either a problem with in the middleware code, or ultimately caused by confusing/sparse language in the docs. I'm happy to post it to stackoverflow or whereever would be more appropriate if this is the wrong area.

How do I get Authorization header into this workflow, possibly in a middleware or more elegant way?

Currently doing it this way, but I know there is a more elegant way. I pull the id token that i got from authenticating from local storage.

// setup at the top
import * as types from '../constants/ActionTypes'
import { CALL_API } from 'redux-api-middleware';
var ls = require('local-storage')

// my action creator
export function fetchLocations(){
  return {
    [CALL_API]: {
      endpoint: 'http://api.somesite.com/api/locations',
      method: 'GET',
      headers: { 'Content-Type': 'application/json', 'Authentication': ls.get('id_token') || '' },
      types: ['REQUEST', 'SUCCESS', 'FAILURE']
    }
  }
}
'''

API calls that dispatch multiple actions for keeping API response data and other stuff (like metadata, etc.) in separate parts of the store? (With sample code)

I'm working on what is basically a CMS and has about ~30 or so different model classes that are rather inter-related. The easiest way to work with them is definitely with normalizr as suggested. Now, for my redux store object, I'm thinking the best way to set things up will be:

store = {
    // Any loaded entities from the API go here
    entities: {
        // merged directly from from normalizr `entities`
        modelA: {
            1: { // ...
            // ...
        },
        modelB: { // ...
            // ...
    },

    // Keep track of all other normal state for managing UI, pagination, etc...
    modelAListView: {
        page: 1,
        selected: [1, 2, 3, 4, 5] // the ID numbers of the objects currently in view
        // ...
    },
    modelBDetailView: {
        result: 17 // id number of modelB in view
    }
    // etc
}

So, my question is, in order to get this to happen with redux-api-middleware, I need a reducer for the state.entities (the apiReducer), and then my individual reducers as normal for all of the different views and such.

But then, I have to dispatch two separate actions to make sure that (A) the apiReducer receives updates whenever the API gets called, and (B) the appropriate model reducer receives updates when the API call involves that particular model.

I have worked out a solution to do this using redux-thunk, but I would really appreciate any feedback on this approach. So far it's working very nicely, and means my actual API calls are super simple to make from within my redux action creators. I would love to know if there is a better way anyone else has come up with!

So, first, here's my helper utility to make API requests with secondary action effects:

// redux/helpers/makeApiRequest.js

// set a default API root (or headers, credentials, etc.) so we don't need to type these everywhere
const apiRoot = '/api/v1';

export function makeApiRequest (options) {
    const {method = 'GET', path, query, schema, secondaryActionTypes, ...rest} = options;

    const endpoint = url.format({query, pathname: path.join(apiRoot, path)});

    let apiAction;

    // return a function that takes dispatch and add the `redux-thunk` middleware
    return dispatch => {
        if (Array.isArray(secondaryActionTypes) && secondaryActionTypes.length === 3) {

            // These are API hits that require a secondary update in a related reducer
            apiAction = {
                [CALL_API]: {
                    method, endpoint,
                    types: [{
                        type: secondaryActionTypes[0],
                        payload: () => dispatch({type: API_REQUEST}),
                    }, {
                        type: secondaryActionTypes[1],
                        payload: (action, state, res) => onSuccess(dispatch, res, schema),
                                                         // see helper function below
                    }, {
                        type: secondaryActionTypes[2],
                        payload: (action, state, res) => onFailure(dispatch, res),
                                                         // see helper function below
                    }],
                },
            };
        } else {

            // This is a normal `redux-api-middleware` type action for actions
            // that don't require updates specifically to the API entities reducer
            apiAction = {[CALL_API]: {method, endpoint, ...rest}};
        }

        return dispatch(apiAction);
    };
}

function onSuccess (dispatch, res, schema) {
    return getJSON(res)
        .then(json => {
            const data = normalize(json, schema);

            // Dispatch the API Action (will merge with `entities` branch of store)
            dispatch({
                type: API_SUCCESS,
                payload: {
                    entities: data.entities,
                },
            });

            // Payload for the secondary action type, will typically be merged into
            // a related model reducer somewhere else in the store
            return {
                result: data.result,
            };
        });
}

function onFailure (dispatch, res) {
    return getJSON(res)
        .then(json => {
            // Same as the default error action from `redux-api-middleware`
            const payload = new ApiError(res.status, res.statusText, json);

            // Send to the API reducer and return for the secondary reducer
            dispatch({type: API_FAILURE, payload});
            return payload;
        });
}

Now the next one is super simple, to update the entities branch of the store:

// redux/reducers/entities.js

const initialState = {
    modelA: {},
    modelB: {},
    // etc.
};

function reducer (state = initialState, action) {
    switch (action.type) {
        case API_REQUEST:
            // ...
        case API_SUCCESS:
            if (action.payload && action.payload.entities) {
                return Object.assign({}, state, action.payload.entities);
            }
            break;
        case API_FAILURE:
            // ...
        default: return state;
    }
}

export default reducer;

Now calling the API from any action is as easy as:

function getModelA (query) {
    const endpoint = 'model_a';

    return apiRequest({
        endpoint, query,
        schema: Schemas.MODEL_A,
        secondaryActionTypes: [MODEL_A_REQEST, MODEL_A_SUCCESS, MODEL_A_FAILURE],
    });
}

and I will have access to all of the data I need in reducers that handle both API actions and MODEL_A related actions.

Comments/feedback/suggestions? Thanks!

Error with Customized Dispatched FSA in Redux Devtools

Redux Devtools shows each action with the current state. It works perfectly when standard FSA are dispatched by redux-api-middleware. However, if a customized FSA is used, the browser will give the following error:

Uncaught (in promise) Error: Invariant Violation: Objects are not valid as a React child (found: object with keys {type, meta}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of LogMonitorAction.
at invariant (http://localhost:3000/vendor.js?0f4c127a85fd659d26ce:708:16)
at traverseAllChildrenImpl (http://localhost:3000/vendor.js?0f4c127a85fd659d26ce:8181:24)

This error is a React rendering error when JSX receives an Object instead of a String for rendering. For standard FSA, action type is a string, devtool renders correctly:

// working
[CALL_API]: { 
  endpoint: 'path/to/endpoint',
  method: 'GET',
  types: [ 'REQUEST', 'SUCCESS', 'FAILURE' ]
}

For customized FSA, action type becomes an object, devtool struggles:

// failed with invariant violation error
[CALL_API]: {
  endpoint: 'path/to/endpoint',
  method: 'GET',
  types: [ // example from documentation
     'REQUEST',
     'SUCCESS', {
      type: 'FAILURE',
      meta: (action, state, res) => {
        if (res) {
          return {
            status: res.status,
            statusText: res.statusText
          };
        } else {
          return {
            status: 'Network request failed'
          };
        }
      }
  }]
}

Disabling devtool will fix the error as well. I would like to know

  1. Is this the correct way of using customized FSA in redux-api-middleware?
  2. Is there a workaround to get customized FSA and redux-devtools working together?
  3. If this is a defect, is it an issue in redux-api-middleware or redux-devtools?

Request type option when passing headers

Hello,

When I pass headers to my POST api call, the method is changing to OPTIONS, is there a reason for this?

Here you can find my code

export function addUser(user) {
  return {
    [CALL_API]: {
      endpoint: API_ROOT + '/users/',
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(user),
      types: [
        types.CREATECOLLECTION_REQUEST,
        {
          type: types.CREATECOLLECTION_SUCCESS,
          payload: (action, state, res) => {
            const contentType = res.headers.get('Content-Type');
            if (contentType && ~contentType.indexOf('json')) {
              // Just making sure res.json() does not raise an error
              return res.json().then((json) => normalize(json, userSchema));
            }
          }
        },
        types.CREATECOLLECTION_FAILURE
      ]
    }
  };
}

Result from the api is

OPTIONS /api/users/ => generated 0 bytes in 2 msecs (HTTP/1.1 204)

/api/users/ => generated 0 bytes in 2 msecs (HTTP/1.1 204)

Test 'apiMiddleware must dispatch an error request FSA on a request error' fails in Windows

The build only failed locally on my Windows machine, but not Travis-CI.

Simply clone the project and run the tests. Tests failed with following error:

apiMiddleware must dispatch an error request FSA on a request error

v next handler called
v dispatched non-error FSA has correct type property
v dispatched non-error FSA has correct payload property
v dispatched non-error FSA has correct meta property
v dispatched non-error FSA has correct error property

x plan != count
----------------
operator: fail
expected: 10
actual: 5

x test exited without ending
-----------------------------
operator: fail
....................
total: 148
passing: 137
failing: 11
duration: 5.5s

Looking into 'apiMiddleware must dispatch an error request FSA on a request error' test, the test does not call the second handler, and all the checks for the error FSA are skipped.

Machine Information:
Windows 7 Enterprise SP1
node: v4.2.1
npm: 2.14.7

API requests and server-side rendering

Hello people,
I am currently looking at how I could implement server-side rendering and I was wondering how redux-api-middleware would play out.

It is very new to me, but If i understand correctly, one way to achieve it is to use async actions via thunks, e.g. redux-thunk, and wait for these actions to build the redux state before serving it to render the html page.

Is it possible to achieve server-side rendering using redux-api-middleware to fetch data ? How would you do it ?

Thank you,

Fork or move to Rackt organisation?

This project (based on redux real-world example) has stirred quite a few people, but currently the author is not responding or allowing PR's. I hope he does return, if only to ask/allow new maintainers to step up.

The last commit was 3 months ago, and right now there are 7 PR's and 13 issues. Besides these people contributing, there are 32 forks and 131 stargazers.

I do feel there is quite some momentum here, especially by the users of the library. For other projects where the users maintain the project i've seen ad-hoc organisations like Rackt bring this together and lower the burden on the owner.

On that note, i wonder what the Rackt crew thinks of redux-api-middleware. So let me CC them here.. @acdlite, @ellbee, @faassen, @gaearon, @jlongster, @knowbody, @mjackson, @mzabriskie, @omnidan, @prattsj, @pwmckenna, @ryanflorence, @taion, @timdorr.

Do you use redux-api-middleware? What is your take on it, and what do you currently use as API middleware?

React Packager freeze

With redux-api-middleware as dependency the packager freezes at 99%. These are my dependencies:

 "dependencies": {
    "react": "=0.14.7",
    "react-native": "=0.18.1",
    "react-native-orientation": "=1.12.2",
    "react-native-vector-icons": "=1.1.0",
    "react-redux": "=4.4.0",
    "redux": "=3.3.1",
    "redux-api-middleware": "^1.0.0-beta3",
    "redux-thunk": "^1.0.3",
    "underscore": "^1.8.3"
  },
  "devDependencies": {
    "babel-cli": "=6.4.5",
    "babel-core": "=6.4.5",
    "babel-eslint": "=4.1.8",
    "babel-preset-es2015": "=6.3.13",
    "babel-preset-react": "=6.3.13",
    "chai": "=3.5.0",
    "eslint": "=1.10.3",
    "eslint-plugin-react": "=3.16.1",
    "eslint-plugin-react-native": "=0.5.0",
    "jsdom": "=8.0.2",
    "mocha": "=2.4.5",
    "react-addons-test-utils": "=0.14.7",
    "redux-logger": "=2.5.0",
    "sinon": "=1.17.3",
    "sinon-chai": "=2.8.0"
  }

Added a console.log at transform function of react-native/packager/transformer and last line is this:

transforming [======================================= ] 98% 611/622transforming <myapp>/node_modules/redux-api-middleware/lib/middleware.js
transforming <myapp>/node_modules/isomorphic-fetch/fetch-npm-browserify.js
transforming [======================================= ] 99% 613/622transforming <myapp>/node_modules/isomorphic-fetch/node_modules/whatwg-fetch/fetch.js
transforming [========================================] 99% 616/622

Allowing `types` to be passed as an object

Just started using redux-api-middleware; really well-thought-out!

A part of the API I don't love is the necessity for types to be a 3-value array. It isn't immediately clear that the order is what distinguishes them, as opposed to a specific naming convention. I'd much rather be able to pass an object, like:

{
  type: 'CATS',
  [CALL_API]: {
    endpoint: 'http://www.example.com/api/users',
    method: 'GET',
    types: {
      request: 'CATS_REQUEST',
      success: 'CATS_RESPONSE',
      failure: 'CATS_ERROR'
    }
  }
}

Would be happy to take a shot at a PR if this is deemed to be a worthwhile addition :)

How to chain actions?

With redux-thunk and explicit calls to the api via your favorite http library (fetch, qwest, etc), it was possible to dispatch other actions in response to an api call succeeding or failing. What is the corresponding technique with redux-api-middleware?

The naive solution would be to allow additional fields on the action object, e.g. onSuccess and onFailure that give you the same inversion of control that redux-thunk does, but I am still very new to this ecosystem, so I am not sure if this is the preferred design pattern.

Another possibility would be some way to observe the redux state and react to changes out-of-band. That way, on MY_ACTION.SUCCESS, I could set the dirty flag in the state, thus causing a new action to be emitted by an observer that was monitoring this state. I feel like I am on the verge of describing redux-rx, but I have not fully integrated the ideas behind FRP yet, so I am just saying words at this point.

Any wisdom on this topic would be greatly appreciated :)

Sending headers is causing API to be called with OPTIONS method

This is likely me not following the documentation correctly, but I'm getting a strange issue.

The following sends a GET request:

[CALL_API]:
 {
      endpoint: "http://my-api.com/api/v2/users",
      method: "GET",
      types: ["REQUEST", "SUCCESS", "FAILURE"],
}

But if I add the headers like so, it changes it to an OPTIONS request and the headers don't appear to be getting sent correctly. If I look at the request, it's actually still sending Accept /.

 [CALL_API]:
 {
      endpoint: "http://my-api.com/api/v2/users",
      method: "GET",
      types: ["REQUEST", "SUCCESS", "FAILURE"],
      headers: {"Accept": "application/json"}
}

Any ideas?

A Different Approach

A was searching a library for API calling for Redux and found Redux API Middleware.
Thank you for sharing this great library.

For my production apps I'm using a different approach: https://github.com/hnordt/reax-api

The usage is very simple. Just add the module as a reducer and dispatch callAPI:

import api, { callAPI } from '@hnordt/reax-api';

const rootReducer = combineReducers({
  // (other reducers)
  api
});

const mapStateToProps = state => {
  const {
    data: projects,
    isLoading = true,
    error
  } = state.api.projects || {};
  return {
    projects,
    isLoading,
    error
  };
};

const mapDispatchToProps = dispatch => ({
  loadProjects: () => dispatch(callAPI('projects', 'GET', '/api/projects'))
});

I made a Twitter post about this: https://twitter.com/hnordt/status/685284990385569792

@agraboso what do you think about this different approach?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.