{"action":"created","issue":{"url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/216","repository_url":"https://api.github.com/repos/agraboso/redux-api-middleware","labels_url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/216/labels{/name}","comments_url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/216/comments","events_url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/216/events","html_url":"https://github.com/agraboso/redux-api-middleware/issues/216","id":381381251,"node_id":"MDU6SXNzdWUzODEzODEyNTE=","number":216,"title":"RFC: `configureCallRSAA` (Callable-RSAA utilities)","user":{"login":"darthrellimnad","id":121980,"node_id":"MDQ6VXNlcjEyMTk4MA==","avatar_url":"https://avatars3.githubusercontent.com/u/121980?v=4","gravatar_id":"","url":"https://api.github.com/users/darthrellimnad","html_url":"https://github.com/darthrellimnad","followers_url":"https://api.github.com/users/darthrellimnad/followers","following_url":"https://api.github.com/users/darthrellimnad/following{/other_user}","gists_url":"https://api.github.com/users/darthrellimnad/gists{/gist_id}","starred_url":"https://api.github.com/users/darthrellimnad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darthrellimnad/subscriptions","organizations_url":"https://api.github.com/users/darthrellimnad/orgs","repos_url":"https://api.github.com/users/darthrellimnad/repos","events_url":"https://api.github.com/users/darthrellimnad/events{/privacy}","received_events_url":"https://api.github.com/users/darthrellimnad/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-11-15T23:20:25Z","updated_at":"2018-12-06T01:02:35Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"> I've been using `redux-api-middleware` for a bit, and ended up writing a few utilities that I've reused a few times across projects.  Lately however, I was thinking about how I could remove the `redux-api-middleware` peerDependency in my utility package to focus it's concerns a bit, but figured I'd first see if any of these seem like a good addition here, before I try to find another way to refactor my projects anyhow.  If there's any interest, I'll dig through my bag and see if there's anything else I can port over that that might be helpful or worth discussion :)\r\n\r\n### Background:\r\nThe first util I'm thinking about porting over is a `configureCallRSAA` util, that's used to create `fetchRSAA` and `fromRSAA` methods that will immediately invoke the `fetch` for any valid RSAA (using apiMiddleware implementation internally) and either return a `[Promise, FSA]` array tuple (fetchRSAA) or an Observable that emits FSAs (fromRSAA).  See JSDoc below fold for more info:\r\n\r\nI originally had a use-case for this when using `redux-api-middleware` alongside `redux-observable`.  Most often when using `redux-api-middleware`, I'll dispatch RSAAs normally to the redux store, allow apiMiddleware to process it, and respond to the REQUEST, SUCCESS, and FAILURE actions in reducers or epics.\r\n\r\nHowever, because of redux-observable issue [#314](https://github.com/redux-observable/redux-observable/issues/314), We can't really \"dispatch\" sequenced RSAAs from a single Epic anymore, only emit them in the result, which meant I usually needed multiple epics or observables to dispatch an API request, forward it to the store to be processed by apiMiddleware, and then respond to the result FSA in the epic... which felt like a bit too much indirection for something that I really just needed `fetch` and `from` for if I avoided my preexisting RSAA action creators we've made for our API.\r\n\r\nI didn't really want to abandon our RSAAs in these situations if I could help it... they are a great way to organize RPC/REST/Legacy api methods in the client, and we still often use them normally by dispatching them directly to the redux store.  Doing so would also mean that I would need to rework any middleware we use that operate on RSAAs or response FSAs  (like our \"authMiddleware\" that applies auth token to headers by reading token from store).\r\n\r\nAfter I tried out various iterations of `configureCallRSAA` to solve the problem, and it became more useful, I found this also allowed me to clean up some \"noise\" in the action log and reducer code for a few API-request-heavy features and avoid store updates until the entire sequence (or parts of the sequence) was complete.  As I tried to generalize the solution, I also realized this would allow you to use `redux-api-middleware` outside of a redux-store context entirely, if desired, and still offer the pre/post-fetch hooks w/ middleware for interceptor-like functionality (similar to angular, axios, etc).\r\n\r\nThis util might be a good fit for `redux-api-middleware` lib, since it would offer users a stable, built-in utility that allows projects using `redux` & `redux-api-middleware` to reuse the existing RSAA spec (and existing action creators or middlewares that leverage it) for situations where direct fetch may be desired.\r\n\r\n### Proposal\r\nHere is the current JSDoc I have for this util that describes this in more detail:\r\n```javascript\r\n/**\r\n * Configure methods for directly fetching via RSAA, without relying on a redux store's\r\n * configured apiMiddleware.\r\n *\r\n * Generally used for custom use-cases, where you'd like more control over async\r\n * action handling for one or more API requests within a context _other_ than the\r\n * redux-api-middleware instance configured with the redux store (e.g. redux-observable,\r\n * or redux-saga). Can be used outside of a redux context entirely if necessary.\r\n *\r\n * ```\r\n * type StoreInterface = { getState: Function, dispatch?: Function }\r\n * type CallRsaaApi = {|\r\n *   fetchRSAA: (rsaa: RSAA, store?: StoreInterface) => [Promise, FSA],\r\n *   fromRSAA: (rsaa: RSAA, store?: StoreInterface) => Observable\r\n * |}\r\n * ```\r\n *\r\n * Example Configuration:\r\n * ```\r\n * // use a tc39 compliant Observable implementation (until natively supported) like RxJS\r\n * import { Observable } from 'rxjs'\r\n *\r\n * // Some app middlewares we'd like to use for RSAAs.\r\n * // These would likely be ones that target the RSAA action type (i.e. isRSAA()).\r\n * const rsaaMiddleware = [\r\n *   authMiddleware,\r\n *   rsaaMetaMiddleware,\r\n * ]\r\n *\r\n * // Middleware that will target the FSAs produced by redux-api-middleware\r\n * const fsaMiddleware = [\r\n *   apiRetryMiddleware\r\n * ]\r\n *\r\n * // configure your store... use the same middleware arrays as above if you'd like :)\r\n * const store = configureStore( ... )\r\n *\r\n * // Then create your callRSAA methods using your desired middleware\r\n * export const { fromRSAA, fetchRSAA } = configureCallRSAA({\r\n *   Observable,\r\n *   rsaaMiddleware,\r\n *   fsaMiddleware,\r\n *   store\r\n * })\r\n * ```\r\n *\r\n * Example Use (w/ redux-observable):\r\n * ```\r\n * // Returns an array whose first value is a Promise for the `fetch` request, and\r\n * // whose second value is the \"request\" FSA.  Promise will resolve the async result\r\n * // FSA.  If you'd like to dispatch the \"request\" action before handling the\r\n * // resolved value, you must do so manually.\r\n * const testFetchRSAA = (action$, state$) =>\r\n *   action$.pipe(\r\n *     ofType('TEST_FETCH_RSAA'),\r\n *     switchMap(() => {\r\n *       const rsaa = rsaaCreator({ foo: 'bar' })\r\n *       const [ promise, request ] = fetchRSAA(rsaa)\r\n *       return from(promise).pipe(startWith(request))\r\n *     })\r\n *   )\r\n *\r\n * // Returns an Observable which will emit the \"request\" and \"success|failure\" FSAs to\r\n * // any subscriptions.  Useful for utilizing rxjs operators that leverage higher order\r\n * // observables, like switchMap.\r\n * const testFromRSAA = (action$, state$) =>\r\n *   action$.pipe(\r\n *     ofType('TEST_FETCH_RSAA'),\r\n *     switchMap(() => {\r\n *       const rsaa = rsaaCreator({ foo: 'bar' })\r\n *       return fromRSAA(rsaa)\r\n *     })\r\n *   )\r\n * ```\r\n *\r\n * @alias module:api\r\n * @param {Observable} Observable tc39 compliant Observable class to use for `fromRSAA`\r\n * @param {function} [apiMiddleware] override the redux-api-middleware with different implementation (useful for mocks/tests, generally not in production!)\r\n * @param {function[]} [fsaMiddleware] list of \"redux\" middleware functions to use for the RSAA's resulting FSAs\r\n * @param {function} [fsaTransform] custom \"transform\" to apply to resulting FSAs from called RSAA\r\n * @param {function[]} [rsaaMiddleware] list of \"redux\" middleware functions process incoming RSAA\r\n * @param {function} [rsaaTransform] custom \"transform\" to apply to incoming RSAA\r\n * @param {{}} [store] a redux store interface. leave `dispatch` method undefined if you wish to avoid dispatching action side-effects to store from configured middleware (recommended).\r\n * @return {CALL_RSAA_API} the 2 \"Call RSAA\" API methods\r\n */\r\n```\r\n### Caveats\r\nThere were a couple of design decisions made to simplify the implementation and focus it's utility on most common/likely use case:\r\n- while `configureCallRSAA` can accept `rsaaMiddleware` and `fsaMiddleware` configurations, these middlewares are _not treated identically_ as a redux middleware configured with the redux Store.  Since we're operating outside of the store's middleware context, and only interested in the request/response behavior of the individual `fetch` request handled by `apiMiddleware`, I limit the # of possible middlewares to those that are synchronous, and only emit a single action to the next middleware (i.e. `next` called only once).  Trying to mimic a redux store's middleware chain point-for-point would be a bit more involved since any potential side-effect or async processing would be allowed, meaning this util would no longer be the `fetch` wrapper I needed.  And if you really needed the identical behavior of a redux store here, I'd recommend using the actual redux store and not using configureCallRSAA :).  That said, I probably still need to improve documentation and error handling around this. May also be worth more discussion, or potentially expanding the # of allowed middlewares with future updates (e.g. allow `next` to be called once async as well?).\r\n- I think (if merged into `redux-api-middleware`) that it might be worth a disclaimer that this isn't intended be an app-wide replacement for redux-api-middleware apps that use something like `redux-observable`... using a redux store's configured apiMiddleware and dispatching the RSAAs individually is still my preferred way to do many things with a REST api and client cache :).  So probably some documentation around intended use might be good, and a point about other techniques you could use before reaching for `fetchRSAA` or `fromRSAA`.\r\n- when using `configureCallRSAA`, I usually provide a `store` value that only provides an interface to the redux store's `getState` method, and leave `dispatch` undefined (generally making any rsaaMiddleware and fsaMiddleware read-only by avoiding property mutations).  However, I couldn't really decide if there might be situations where you want a `fetchRSAA` or `fromRSAA` method to invoke a `dispatch` to the real redux store from a configured middleware... 🤔 .  So this is allowed, but in practice I usually avoid it unless I'm refactoring an older middleware function that may have used `dispatch` directly.\r\n- any new utils may add to package size of `redux-api-middleware` lib, which is not a huge concern for many apps built using unused code removal and tree shaking, but could be an issue for some.\r\n- i don't think I ever really figured out a behavior to use when `bailout` is `true` w/ the callRSAA methods 🤔.  In practice, I don't ever use it with RSAAs invoked by `fromRSAA` or `fetchRSAA` but might be worth discussion?"},"comment":{"url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/comments/444709586","html_url":"https://github.com/agraboso/redux-api-middleware/issues/216#issuecomment-444709586","issue_url":"https://api.github.com/repos/agraboso/redux-api-middleware/issues/216","id":444709586,"node_id":"MDEyOklzc3VlQ29tbWVudDQ0NDcwOTU4Ng==","user":{"login":"darthrellimnad","id":121980,"node_id":"MDQ6VXNlcjEyMTk4MA==","avatar_url":"https://avatars3.githubusercontent.com/u/121980?v=4","gravatar_id":"","url":"https://api.github.com/users/darthrellimnad","html_url":"https://github.com/darthrellimnad","followers_url":"https://api.github.com/users/darthrellimnad/followers","following_url":"https://api.github.com/users/darthrellimnad/following{/other_user}","gists_url":"https://api.github.com/users/darthrellimnad/gists{/gist_id}","starred_url":"https://api.github.com/users/darthrellimnad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darthrellimnad/subscriptions","organizations_url":"https://api.github.com/users/darthrellimnad/orgs","repos_url":"https://api.github.com/users/darthrellimnad/repos","events_url":"https://api.github.com/users/darthrellimnad/events{/privacy}","received_events_url":"https://api.github.com/users/darthrellimnad/received_events","type":"User","site_admin":false},"created_at":"2018-12-06T01:02:35Z","updated_at":"2018-12-06T01:02:35Z","author_association":"CONTRIBUTOR","body":"Lately I've also been considering removing the `fetchRSAA` requirement from this util, and only making a `configureFromRSAA` util that returns an Observable.  This has the benefit of allowing me to be less restrictive about the types of middlewares I can configure for it, since the Observable can emit more than 1 action, unlike `fetchRSAA`, which was designed to return Promises.\r\n\r\nThis would simplify things quite a bit actually, and slim down the code... but wouldn't offer the \"Promise\" based utility if anyone thinks that would still be helpful (but we could later add a `configureFetchRSAA` util if desired)."}}
{"actor":{"display_login":"darthrellimnad"}}