├── .babelrc ├── .gitignore ├── src └── index.js ├── webpack.config.js ├── package.json ├── lib ├── fetch-middleware.min.js └── fetch-middleware.min.js.map └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["babel-plugin-add-module-exports", "transform-object-rest-spread"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 3 | node_modules 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const isArray = type => Array.isArray(type) 2 | 3 | const isFunc = val => typeof val === 'function' 4 | 5 | export default function(store) { 6 | const {dispatch} = store 7 | 8 | return next => action => { 9 | const {type, payload} = action 10 | 11 | if (!isArray(type) || !type.every(isFunc) || !isFunc(payload.data)) { 12 | return next(action) 13 | } 14 | 15 | const [requesting, success, failure] = type 16 | 17 | dispatch(requesting()) 18 | 19 | const request = payload.data() 20 | 21 | return isFunc(request.then) 22 | ? request.then( 23 | response => dispatch(success({...payload, data: response.data})), 24 | error => dispatch(failure({payload: error, error: true})) 25 | ) 26 | : next(action) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; 3 | var path = require('path'); 4 | var env = require('yargs').argv.mode; 5 | 6 | var libraryName = 'fetchMiddleware'; 7 | var fileName = 'fetch-middleware'; 8 | 9 | var plugins = [], outputFile; 10 | 11 | if (env === 'build') { 12 | plugins.push(new UglifyJsPlugin({ minimize: true })); 13 | outputFile = fileName + '.min.js'; 14 | } else { 15 | outputFile = fileName + '.js'; 16 | } 17 | 18 | var config = { 19 | entry: __dirname + '/src/index.js', 20 | devtool: 'source-map', 21 | output: { 22 | path: __dirname + '/lib', 23 | filename: outputFile, 24 | library: libraryName, 25 | libraryTarget: 'umd', 26 | umdNamedDefine: true 27 | }, 28 | module: { 29 | loaders: [ 30 | { 31 | test: /(\.jsx|\.js)$/, 32 | loader: 'babel', 33 | exclude: /(node_modules|bower_components)/ 34 | } 35 | ] 36 | }, 37 | resolve: { 38 | root: path.resolve('./src'), 39 | extensions: ['', '.js'] 40 | }, 41 | plugins: plugins 42 | }; 43 | 44 | module.exports = config; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetch-middleware", 3 | "version": "1.1.0", 4 | "description": "Middleware for Redux async actions", 5 | "license": "MIT", 6 | "author": "Junior Oliveira", 7 | "main": "lib/fetch-middleware.min.js", 8 | "scripts": { 9 | "build": "webpack --mode=build", 10 | "dev": "webpack --progress --colors --watch --mode=dev" 11 | }, 12 | "devDependencies": { 13 | "babel-core": "^6.24.1", 14 | "babel-loader": "^6.4.1", 15 | "babel-plugin-add-module-exports": "0.1.2", 16 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 17 | "babel-preset-es2015": "6.3.13", 18 | "chai": "3.4.1", 19 | "webpack": "1.12.9", 20 | "yargs": "3.32.0" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/arojunior/fetch-middleware.git" 25 | }, 26 | "keywords": [ 27 | "redux", 28 | "es6", 29 | "middleware", 30 | "async", 31 | "requests", 32 | "flux", 33 | "side-effects" 34 | ], 35 | "bugs": { 36 | "url": "https://github.com/arojunior/fetch-middleware/issues" 37 | }, 38 | "homepage": "https://github.com/arojunior/fetch-middleware" 39 | } 40 | -------------------------------------------------------------------------------- /lib/fetch-middleware.min.js: -------------------------------------------------------------------------------- 1 | !function(r,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("fetchMiddleware",[],t):"object"==typeof exports?exports.fetchMiddleware=t():r.fetchMiddleware=t()}(this,function(){return function(r){function t(n){if(e[n])return e[n].exports;var o=e[n]={exports:{},id:n,loaded:!1};return r[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var e={};return t.m=r,t.c=e,t.p="",t(0)}([function(r,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var e=Object.assign||function(r){for(var t=1;t ({ 22 | type : [ 23 | githubFetching, 24 | githubSuccess, 25 | githubError 26 | ], 27 | payload : { 28 | data : () => axios.get(`https://api.github.com/users/${username}`) 29 | } 30 | }) 31 | ``` 32 | * **type** is an Array with actions creators 33 | * **payload** must have **data** property and it must to be a function that returns a **Promise** 34 | 35 | **Note:** you can pass more properties inside **payload** and it will be passed to success action. 36 | 37 | Basic flow: loginSending -> data -> loginSuccess / loginError 38 | 39 | The middleware will call loginSending before apiCall and loginSuccess when promise got resolved, if got an error, loginError will be called. 40 | 41 | ### Reducer / Action creators example (with redux-actions) 42 | 43 | * **Actions types** 44 | 45 | ```javascript 46 | export const GITHUB_FETCHING = 'modules/Github/FETCHING' 47 | export const GITHUB_SUCCESS = 'modules/Github/SUCCESS' 48 | export const GITHUB_ERROR = 'modules/Github/ERROR' 49 | ``` 50 | 51 | * **Reducer** 52 | 53 | ```javascript 54 | import {handleActions} from 'redux-actions' 55 | 56 | import {GITHUB_FETCHING, GITHUB_SUCCESS, GITHUB_ERROR} from './actions' 57 | 58 | const initialState = { 59 | fetching : false, 60 | user : null 61 | } 62 | 63 | const reducer = handleActions({ 64 | [GITHUB_FETCHING]: (state, action) => ({ 65 | ...state, 66 | sending : true 67 | }), 68 | 69 | [GITHUB_SUCCESS]: (state, action) => ({ 70 | sending : false, 71 | user : action.payload.data 72 | }), 73 | 74 | [GITHUB_ERROR]: (state, action) => ({ 75 | ...state, 76 | sending : false, 77 | }) 78 | 79 | }, initialState); 80 | 81 | export default reducer 82 | ``` 83 | 84 | ### Example without redux-actions 85 | 86 | ```javascript 87 | const githubFetching = () => ({ type : 'GITHUB_FETCHING' }) 88 | const githubError = () => ({ type : 'GITHUB_ERROR' }) 89 | const githubSuccess = payload => ({ 90 | type : 'GITHUB_SUCCESS', 91 | payload : payload 92 | }) 93 | 94 | const initialState = { 95 | fetching : false, 96 | user : null 97 | } 98 | 99 | export const getUserFromGithub = username => ({ 100 | type : [ 101 | githubFetching, 102 | githubSuccess, 103 | githubError 104 | ], 105 | payload : { 106 | data : () => axios.get(`https://api.github.com/users/${username}`) 107 | } 108 | }) 109 | 110 | export default (state = initialState, action) => { 111 | switch (action.type) { 112 | case 'GITHUB_FETCHING': 113 | return {...state, 114 | fetching : true 115 | } 116 | case 'GITHUB_SUCCESS': 117 | return {...state, 118 | fetching : false, 119 | user : action.payload.data 120 | } 121 | case 'GITHUB_ERROR': 122 | return {...state, 123 | fetching : false 124 | } 125 | default: 126 | return state 127 | } 128 | } 129 | ``` 130 | 131 | **Another Note:** You have to pass the default methods to dispatch in case of success or failure, but the middleware will always return a Promise. 132 | So if you need to do something after dispatch these methods, you are able to use: 133 | 134 | ```javascript 135 | dispatch(myAction()) 136 | // here we got the dispatch of success or failure and then... 137 | .then() 138 | .catch() 139 | // and so on 140 | ``` 141 | -------------------------------------------------------------------------------- /lib/fetch-middleware.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///fetch-middleware.min.js","webpack:///webpack/bootstrap 5c23d4d134f6e76d1055","webpack:///D:/Junior/Dropbox/compartilhado/Github/fetch-middleware/src/index.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","Object","defineProperty","value","_extends","assign","target","i","arguments","length","source","key","prototype","hasOwnProperty","_slicedToArray","sliceIterator","arr","_arr","_n","_d","_e","undefined","_s","_i","Symbol","iterator","next","done","push","err","Array","isArray","TypeError","store","dispatch","action","type","payload","every","isFunc","data","_type","requesting","success","failure","request","then","response","error","val"],"mappings":"CAAA,SAAAA,EAAAC,GACA,gBAAAC,UAAA,gBAAAC,QACAA,OAAAD,QAAAD,IACA,kBAAAG,gBAAAC,IACAD,OAAA,qBAAAH,GACA,gBAAAC,SACAA,QAAA,gBAAAD,IAEAD,EAAA,gBAAAC,KACCK,KAAA,WACD,MCAgB,UAAUC,GCN1B,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAP,OAGA,IAAAC,GAAAO,EAAAD,IACAP,WACAS,GAAAF,EACAG,QAAA,EAUA,OANAL,GAAAE,GAAAI,KAAAV,EAAAD,QAAAC,IAAAD,QAAAM,GAGAL,EAAAS,QAAA,EAGAT,EAAAD,QAvBA,GAAAQ,KAqCA,OATAF,GAAAM,EAAAP,EAGAC,EAAAO,EAAAL,EAGAF,EAAAQ,EAAA,GAGAR,EAAA,KDgBM,SAASL,EAAQD,GAEtB,YAEAe,QAAOC,eAAehB,EAAS,cAC7BiB,OAAO,GAGT,IAAIC,GAAWH,OAAOI,QAAU,SAAUC,GAAU,IAAK,GAAIC,GAAI,EAAGA,EAAIC,UAAUC,OAAQF,IAAK,CAAE,GAAIG,GAASF,UAAUD,EAAI,KAAK,GAAII,KAAOD,GAAcT,OAAOW,UAAUC,eAAehB,KAAKa,EAAQC,KAAQL,EAAOK,GAAOD,EAAOC,IAAY,MAAOL,IAEnPQ,EAAiB,WAAc,QAASC,GAAcC,EAAKT,GAAK,GAAIU,MAAeC,GAAK,EAAUC,GAAK,EAAWC,EAAKC,MAAW,KAAM,IAAK,GAAiCC,GAA7BC,EAAKP,EAAIQ,OAAOC,cAAmBP,GAAMI,EAAKC,EAAGG,QAAQC,QAAoBV,EAAKW,KAAKN,EAAGnB,QAAYI,GAAKU,EAAKR,SAAWF,GAA3DW,GAAK,IAAoE,MAAOW,GAAOV,GAAK,EAAMC,EAAKS,EAAO,QAAU,KAAWX,GAAMK,EAAG,WAAWA,EAAG,YAAe,QAAU,GAAIJ,EAAI,KAAMC,IAAQ,MAAOH,GAAQ,MAAO,UAAUD,EAAKT,GAAK,GAAIuB,MAAMC,QAAQf,GAAQ,MAAOA,EAAY,IAAIQ,OAAOC,WAAYxB,QAAOe,GAAQ,MAAOD,GAAcC,EAAKT,EAAa,MAAM,IAAIyB,WAAU,2DAEtlB9C,cE9Dc,SAAS+C,GAAO,GACtBC,GAAYD,EAAZC,QAEP,OAAO,UAAAR,GAAA,MAAQ,UAAAS,GAAU,GAChBC,GAAiBD,EAAjBC,KAAMC,EAAWF,EAAXE,OAEb,KAAKN,EAAQK,KAAUA,EAAKE,MAAMC,KAAYA,EAAOF,EAAQG,MAC3D,MAAOd,GAAKS,EAJS,IAAAM,GAAA3B,EAOgBsB,EAPhB,GAOhBM,EAPgBD,EAAA,GAOJE,EAPIF,EAAA,GAOKG,EAPLH,EAAA,EASvBP,GAASQ,IAET,IAAMG,GAAUR,EAAQG,MAExB,OAAOD,GAAOM,EAAQC,MAClBD,EAAQC,KACN,SAAAC,GAAA,MAAYb,GAASS,OAAYN,GAASG,KAAMO,EAASP,UACzD,SAAAQ,GAAA,MAASd,GAASU,GAASP,QAASW,EAAOA,OAAO,OAEpDtB,EAAKS,KAzBb,IAAMJ,GAAU,SAAAK,GAAA,MAAQN,OAAMC,QAAQK,IAEhCG,EAAS,SAAAU,GAAA,MAAsB,kBAARA,GFwG5B9D,GAAOD,QAAUA,EAAQ","file":"fetch-middleware.min.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"fetchMiddleware\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"fetchMiddleware\"] = factory();\n\telse\n\t\troot[\"fetchMiddleware\"] = factory();\n})(this, function() {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"fetchMiddleware\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"fetchMiddleware\"] = factory();\n\telse\n\t\troot[\"fetchMiddleware\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\t\n\tvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\t\n\tvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\t\n\texports.default = function (store) {\n\t var dispatch = store.dispatch;\n\t\n\t\n\t return function (next) {\n\t return function (action) {\n\t var type = action.type,\n\t payload = action.payload;\n\t\n\t\n\t if (!isArray(type) || !type.every(isFunc) || !isFunc(payload.data)) {\n\t return next(action);\n\t }\n\t\n\t var _type = _slicedToArray(type, 3),\n\t requesting = _type[0],\n\t success = _type[1],\n\t failure = _type[2];\n\t\n\t dispatch(requesting());\n\t\n\t var request = payload.data();\n\t\n\t return isFunc(request.then) ? request.then(function (response) {\n\t return dispatch(success(_extends({}, payload, { data: response.data })));\n\t }, function (error) {\n\t return dispatch(failure({ payload: error, error: true }));\n\t }) : next(action);\n\t };\n\t };\n\t};\n\t\n\tvar isArray = function isArray(type) {\n\t return Array.isArray(type);\n\t};\n\t\n\tvar isFunc = function isFunc(val) {\n\t return typeof val === 'function';\n\t};\n\t\n\tmodule.exports = exports['default'];\n\n/***/ }\n/******/ ])\n});\n;\n\n\n/** WEBPACK FOOTER **\n ** fetch-middleware.min.js\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 5c23d4d134f6e76d1055\n **/","const isArray = type => Array.isArray(type)\n\nconst isFunc = val => typeof val === 'function'\n\nexport default function(store) {\n const {dispatch} = store\n\n return next => action => {\n const {type, payload} = action\n\n if (!isArray(type) || !type.every(isFunc) || !isFunc(payload.data)) {\n return next(action)\n }\n\n const [requesting, success, failure] = type\n\n dispatch(requesting())\n\n const request = payload.data()\n\n return isFunc(request.then)\n ? request.then(\n response => dispatch(success({...payload, data: response.data})),\n error => dispatch(failure({payload: error, error: true}))\n )\n : next(action)\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** D:/Junior/Dropbox/compartilhado/Github/fetch-middleware/src/index.js\n **/"],"sourceRoot":""} --------------------------------------------------------------------------------