redux-api-middleware
Advanced tools
Comparing version 2.3.0 to 3.0.0-beta.0
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.apiMiddleware = exports.getJSON = exports.ApiError = exports.RequestError = exports.InternalError = exports.InvalidRSAA = exports.isValidRSAA = exports.validateRSAA = exports.isRSAA = exports.RSAA = exports.CALL_API = undefined; | ||
exports.apiMiddleware = exports.createMiddleware = exports.getJSON = exports.ApiError = exports.RequestError = exports.InternalError = exports.InvalidRSAA = exports.isValidRSAA = exports.validateRSAA = exports.isRSAA = exports.RSAA = undefined; | ||
@@ -23,3 +23,2 @@ var _RSAA = require('./RSAA'); | ||
exports.CALL_API = _RSAA2.default; | ||
exports.RSAA = _RSAA2.default; | ||
@@ -34,2 +33,3 @@ exports.isRSAA = _validation.isRSAA; | ||
exports.getJSON = _util.getJSON; | ||
exports.createMiddleware = _middleware.createMiddleware; | ||
exports.apiMiddleware = _middleware.apiMiddleware; /** | ||
@@ -40,3 +40,2 @@ * Redux middleware for calling an API | ||
* @exports {string} RSAA | ||
* @exports {string} CALL_API - alias of RSAA, to be deprecated in v3 | ||
* @exports {function} isRSAA | ||
@@ -50,2 +49,3 @@ * @exports {function} validateRSAA | ||
* @exports {function} getJSON | ||
* @exports {function} createMiddleware | ||
* @exports {ReduxMiddleWare} apiMiddleware | ||
@@ -52,0 +52,0 @@ */ |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.apiMiddleware = undefined; | ||
exports.apiMiddleware = exports.createMiddleware = undefined; | ||
@@ -25,2 +25,6 @@ var _regenerator = require('babel-runtime/regenerator'); | ||
var _assign = require('babel-runtime/core-js/object/assign'); | ||
var _assign2 = _interopRequireDefault(_assign); | ||
var _RSAA = require('./RSAA'); | ||
@@ -39,271 +43,313 @@ | ||
/** | ||
* A Redux middleware that processes RSAA actions. | ||
* Default options for redux-api-middleware | ||
* These can be customized by passing options into `createMiddleware` | ||
* @type {Object} | ||
*/ | ||
var defaults = { | ||
ok: function ok(res) { | ||
return res.ok; | ||
}, | ||
fetch: fetch | ||
}; | ||
/** | ||
* A middleware creator used to create a ReduxApiMiddleware | ||
* with custom defaults | ||
* | ||
* @type {ReduxMiddleware} | ||
* @type {function} | ||
* @returns {ReduxMiddleware} | ||
* @access public | ||
*/ | ||
function apiMiddleware(_ref) { | ||
function createMiddleware() { | ||
var _this = this; | ||
var getState = _ref.getState; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return function (next) { | ||
return function (action) { | ||
// Do not process actions without an [RSAA] property | ||
if (!(0, _validation.isRSAA)(action)) { | ||
return next(action); | ||
} | ||
var middlewareOptions = (0, _assign2.default)({}, defaults, options); | ||
return (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee() { | ||
var validationErrors, _callAPI, _requestType, callAPI, endpoint, body, headers, _callAPI$options, options, _callAPI$fetch, doFetch, method, credentials, bailout, types, _normalizeTypeDescrip, _normalizeTypeDescrip2, requestType, successType, failureType, res; | ||
return function (_ref) { | ||
var getState = _ref.getState; | ||
return function (next) { | ||
return function (action) { | ||
// Do not process actions without an [RSAA] property | ||
if (!(0, _validation.isRSAA)(action)) { | ||
return next(action); | ||
} | ||
return _regenerator2.default.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
// Try to dispatch an error request FSA for invalid RSAAs | ||
validationErrors = (0, _validation.validateRSAA)(action); | ||
return (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee() { | ||
var validationErrors, _callAPI, _requestType, callAPI, endpoint, body, headers, _callAPI$options, options, _callAPI$fetch, doFetch, _callAPI$ok, ok, method, credentials, bailout, types, _normalizeTypeDescrip, _normalizeTypeDescrip2, requestType, successType, failureType, res, isOk; | ||
if (!validationErrors.length) { | ||
_context.next = 5; | ||
break; | ||
} | ||
return _regenerator2.default.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
// Try to dispatch an error request FSA for invalid RSAAs | ||
validationErrors = (0, _validation.validateRSAA)(action); | ||
_callAPI = action[_RSAA2.default]; | ||
if (!validationErrors.length) { | ||
_context.next = 5; | ||
break; | ||
} | ||
if (_callAPI.types && Array.isArray(_callAPI.types)) { | ||
_requestType = _callAPI.types[0]; | ||
_callAPI = action[_RSAA2.default]; | ||
if (_requestType && _requestType.type) { | ||
_requestType = _requestType.type; | ||
if (_callAPI.types && Array.isArray(_callAPI.types)) { | ||
_requestType = _callAPI.types[0]; | ||
if (_requestType && _requestType.type) { | ||
_requestType = _requestType.type; | ||
} | ||
next({ | ||
type: _requestType, | ||
payload: new _errors.InvalidRSAA(validationErrors), | ||
error: true | ||
}); | ||
} | ||
next({ | ||
type: _requestType, | ||
payload: new _errors.InvalidRSAA(validationErrors), | ||
error: true | ||
}); | ||
} | ||
return _context.abrupt('return'); | ||
return _context.abrupt('return'); | ||
case 5: | ||
case 5: | ||
// Parse the validated RSAA action | ||
callAPI = action[_RSAA2.default]; | ||
endpoint = callAPI.endpoint, body = callAPI.body, headers = callAPI.headers, _callAPI$options = callAPI.options, options = _callAPI$options === undefined ? {} : _callAPI$options, _callAPI$fetch = callAPI.fetch, doFetch = _callAPI$fetch === undefined ? fetch : _callAPI$fetch; | ||
method = callAPI.method, credentials = callAPI.credentials, bailout = callAPI.bailout, types = callAPI.types; | ||
_normalizeTypeDescrip = (0, _util.normalizeTypeDescriptors)(types), _normalizeTypeDescrip2 = (0, _slicedToArray3.default)(_normalizeTypeDescrip, 3), requestType = _normalizeTypeDescrip2[0], successType = _normalizeTypeDescrip2[1], failureType = _normalizeTypeDescrip2[2]; | ||
// Parse the validated RSAA action | ||
callAPI = action[_RSAA2.default]; | ||
endpoint = callAPI.endpoint, body = callAPI.body, headers = callAPI.headers, _callAPI$options = callAPI.options, options = _callAPI$options === undefined ? {} : _callAPI$options, _callAPI$fetch = callAPI.fetch, doFetch = _callAPI$fetch === undefined ? middlewareOptions.fetch : _callAPI$fetch, _callAPI$ok = callAPI.ok, ok = _callAPI$ok === undefined ? middlewareOptions.ok : _callAPI$ok; | ||
method = callAPI.method, credentials = callAPI.credentials, bailout = callAPI.bailout, types = callAPI.types; | ||
_normalizeTypeDescrip = (0, _util.normalizeTypeDescriptors)(types), _normalizeTypeDescrip2 = (0, _slicedToArray3.default)(_normalizeTypeDescrip, 3), requestType = _normalizeTypeDescrip2[0], successType = _normalizeTypeDescrip2[1], failureType = _normalizeTypeDescrip2[2]; | ||
// Should we bail out? | ||
// Should we bail out? | ||
_context.prev = 9; | ||
_context.prev = 9; | ||
if (!(typeof bailout === 'boolean' && bailout || typeof bailout === 'function' && bailout(getState()))) { | ||
_context.next = 12; | ||
if (!(typeof bailout === 'boolean' && bailout || typeof bailout === 'function' && bailout(getState()))) { | ||
_context.next = 12; | ||
break; | ||
} | ||
return _context.abrupt('return'); | ||
case 12: | ||
_context.next = 21; | ||
break; | ||
} | ||
return _context.abrupt('return'); | ||
case 14: | ||
_context.prev = 14; | ||
_context.t0 = _context['catch'](9); | ||
_context.t1 = next; | ||
_context.next = 19; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].bailout function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 12: | ||
_context.next = 21; | ||
break; | ||
case 19: | ||
_context.t2 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t1)(_context.t2)); | ||
case 14: | ||
_context.prev = 14; | ||
_context.t0 = _context['catch'](9); | ||
_context.t1 = next; | ||
_context.next = 19; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].bailout function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 21: | ||
if (!(typeof endpoint === 'function')) { | ||
_context.next = 33; | ||
break; | ||
} | ||
case 19: | ||
_context.t2 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t1)(_context.t2)); | ||
_context.prev = 22; | ||
case 21: | ||
if (!(typeof endpoint === 'function')) { | ||
endpoint = endpoint(getState()); | ||
_context.next = 33; | ||
break; | ||
} | ||
_context.prev = 22; | ||
case 26: | ||
_context.prev = 26; | ||
_context.t3 = _context['catch'](22); | ||
_context.t4 = next; | ||
_context.next = 31; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].endpoint function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
endpoint = endpoint(getState()); | ||
_context.next = 33; | ||
break; | ||
case 31: | ||
_context.t5 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t4)(_context.t5)); | ||
case 26: | ||
_context.prev = 26; | ||
_context.t3 = _context['catch'](22); | ||
_context.t4 = next; | ||
_context.next = 31; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].endpoint function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 33: | ||
if (!(typeof body === 'function')) { | ||
_context.next = 45; | ||
break; | ||
} | ||
case 31: | ||
_context.t5 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t4)(_context.t5)); | ||
_context.prev = 34; | ||
case 33: | ||
if (!(typeof body === 'function')) { | ||
body = body(getState()); | ||
_context.next = 45; | ||
break; | ||
} | ||
_context.prev = 34; | ||
case 38: | ||
_context.prev = 38; | ||
_context.t6 = _context['catch'](34); | ||
_context.t7 = next; | ||
_context.next = 43; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].body function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
body = body(getState()); | ||
_context.next = 45; | ||
break; | ||
case 43: | ||
_context.t8 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t7)(_context.t8)); | ||
case 38: | ||
_context.prev = 38; | ||
_context.t6 = _context['catch'](34); | ||
_context.t7 = next; | ||
_context.next = 43; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].body function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 45: | ||
if (!(typeof headers === 'function')) { | ||
_context.next = 57; | ||
break; | ||
} | ||
case 43: | ||
_context.t8 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t7)(_context.t8)); | ||
_context.prev = 46; | ||
case 45: | ||
if (!(typeof headers === 'function')) { | ||
headers = headers(getState()); | ||
_context.next = 57; | ||
break; | ||
} | ||
_context.prev = 46; | ||
case 50: | ||
_context.prev = 50; | ||
_context.t9 = _context['catch'](46); | ||
_context.t10 = next; | ||
_context.next = 55; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].headers function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
headers = headers(getState()); | ||
_context.next = 57; | ||
break; | ||
case 55: | ||
_context.t11 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t10)(_context.t11)); | ||
case 50: | ||
_context.prev = 50; | ||
_context.t9 = _context['catch'](46); | ||
_context.t10 = next; | ||
_context.next = 55; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].headers function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 57: | ||
if (!(typeof options === 'function')) { | ||
_context.next = 69; | ||
break; | ||
} | ||
case 55: | ||
_context.t11 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t10)(_context.t11)); | ||
_context.prev = 58; | ||
case 57: | ||
if (!(typeof options === 'function')) { | ||
options = options(getState()); | ||
_context.next = 69; | ||
break; | ||
} | ||
_context.prev = 58; | ||
case 62: | ||
_context.prev = 62; | ||
_context.t12 = _context['catch'](58); | ||
_context.t13 = next; | ||
_context.next = 67; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].options function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
options = options(getState()); | ||
_context.next = 69; | ||
break; | ||
case 67: | ||
_context.t14 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t13)(_context.t14)); | ||
case 62: | ||
_context.prev = 62; | ||
_context.t12 = _context['catch'](58); | ||
_context.t13 = next; | ||
_context.next = 67; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError('[RSAA].options function failed'), | ||
error: true | ||
}), [action, getState()]); | ||
case 69: | ||
if (!(typeof requestType.payload === 'function' || typeof requestType.meta === 'function')) { | ||
_context.next = 77; | ||
break; | ||
} | ||
case 67: | ||
_context.t14 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t13)(_context.t14)); | ||
_context.t15 = next; | ||
_context.next = 73; | ||
return (0, _util.actionWith)(requestType, [action, getState()]); | ||
case 69: | ||
if (!(typeof requestType.payload === 'function' || typeof requestType.meta === 'function')) { | ||
_context.next = 77; | ||
case 73: | ||
_context.t16 = _context.sent; | ||
(0, _context.t15)(_context.t16); | ||
_context.next = 78; | ||
break; | ||
} | ||
_context.t15 = next; | ||
_context.next = 73; | ||
return (0, _util.actionWith)(requestType, [action, getState()]); | ||
case 77: | ||
next(requestType); | ||
case 73: | ||
_context.t16 = _context.sent; | ||
(0, _context.t15)(_context.t16); | ||
_context.next = 78; | ||
break; | ||
case 78: | ||
res = void 0; | ||
_context.prev = 79; | ||
_context.next = 82; | ||
return doFetch(endpoint, (0, _extends3.default)({}, options, { | ||
method: method, | ||
body: body || undefined, | ||
credentials: credentials, | ||
headers: headers || {} | ||
})); | ||
case 77: | ||
next(requestType); | ||
case 82: | ||
res = _context.sent; | ||
_context.next = 92; | ||
break; | ||
case 78: | ||
_context.prev = 78; | ||
_context.next = 81; | ||
return doFetch(endpoint, (0, _extends3.default)({}, options, { | ||
method: method, | ||
body: body || undefined, | ||
credentials: credentials, | ||
headers: headers || {} | ||
})); | ||
case 85: | ||
_context.prev = 85; | ||
_context.t17 = _context['catch'](79); | ||
_context.t18 = next; | ||
_context.next = 90; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, failureType, { | ||
payload: new _errors.RequestError(_context.t17.message), | ||
error: true | ||
}), [action, getState()]); | ||
case 81: | ||
res = _context.sent; | ||
_context.next = 91; | ||
break; | ||
case 90: | ||
_context.t19 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t18)(_context.t19)); | ||
case 84: | ||
_context.prev = 84; | ||
_context.t17 = _context['catch'](78); | ||
_context.t18 = next; | ||
_context.next = 89; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, requestType, { | ||
payload: new _errors.RequestError(_context.t17.message), | ||
error: true | ||
}), [action, getState()]); | ||
case 92: | ||
isOk = void 0; | ||
_context.prev = 93; | ||
case 89: | ||
_context.t19 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t18)(_context.t19)); | ||
case 91: | ||
if (!res.ok) { | ||
_context.next = 99; | ||
isOk = ok(res); | ||
_context.next = 104; | ||
break; | ||
} | ||
_context.t20 = next; | ||
_context.next = 95; | ||
return (0, _util.actionWith)(successType, [action, getState(), res]); | ||
case 97: | ||
_context.prev = 97; | ||
_context.t20 = _context['catch'](93); | ||
_context.t21 = next; | ||
_context.next = 102; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, failureType, { | ||
payload: new _errors.InternalError('[RSAA].ok function failed'), | ||
error: true | ||
}), [action, getState(), res]); | ||
case 95: | ||
_context.t21 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t20)(_context.t21)); | ||
case 102: | ||
_context.t22 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t21)(_context.t22)); | ||
case 99: | ||
_context.t22 = next; | ||
_context.next = 102; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, failureType, { | ||
error: true | ||
}), [action, getState(), res]); | ||
case 104: | ||
if (!isOk) { | ||
_context.next = 112; | ||
break; | ||
} | ||
case 102: | ||
_context.t23 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t22)(_context.t23)); | ||
_context.t23 = next; | ||
_context.next = 108; | ||
return (0, _util.actionWith)(successType, [action, getState(), res]); | ||
case 104: | ||
case 'end': | ||
return _context.stop(); | ||
case 108: | ||
_context.t24 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t23)(_context.t24)); | ||
case 112: | ||
_context.t25 = next; | ||
_context.next = 115; | ||
return (0, _util.actionWith)((0, _extends3.default)({}, failureType, { | ||
error: true | ||
}), [action, getState(), res]); | ||
case 115: | ||
_context.t26 = _context.sent; | ||
return _context.abrupt('return', (0, _context.t25)(_context.t26)); | ||
case 117: | ||
case 'end': | ||
return _context.stop(); | ||
} | ||
} | ||
} | ||
}, _callee, _this, [[9, 14], [22, 26], [34, 38], [46, 50], [58, 62], [78, 84]]); | ||
}))(); | ||
}, _callee, _this, [[9, 14], [22, 26], [34, 38], [46, 50], [58, 62], [79, 85], [93, 97]]); | ||
}))(); | ||
}; | ||
}; | ||
@@ -313,2 +359,15 @@ }; | ||
/** | ||
* A Redux middleware that processes RSAA actions. | ||
* | ||
* @type {ReduxMiddleware} | ||
* @access public | ||
*/ | ||
function apiMiddleware(_ref3) { | ||
var getState = _ref3.getState; | ||
return createMiddleware()({ getState: getState }); | ||
} | ||
exports.createMiddleware = createMiddleware; | ||
exports.apiMiddleware = apiMiddleware; |
@@ -77,3 +77,3 @@ 'use strict'; | ||
var validationErrors = []; | ||
var validCallAPIKeys = ['endpoint', 'options', 'method', 'body', 'headers', 'credentials', 'bailout', 'types', 'fetch']; | ||
var validCallAPIKeys = ['endpoint', 'options', 'method', 'body', 'headers', 'credentials', 'bailout', 'types', 'fetch', 'ok']; | ||
var validMethods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']; | ||
@@ -87,8 +87,2 @@ var validCredentials = ['omit', 'same-origin', 'include']; | ||
for (var key in action) { | ||
if (key !== _RSAA2.default) { | ||
validationErrors.push('Invalid root key: ' + key); | ||
} | ||
} | ||
var callAPI = action[_RSAA2.default]; | ||
@@ -98,5 +92,5 @@ if (!(0, _lodash2.default)(callAPI)) { | ||
} | ||
for (var _key in callAPI) { | ||
if (!~validCallAPIKeys.indexOf(_key)) { | ||
validationErrors.push('Invalid [RSAA] key: ' + _key); | ||
for (var key in callAPI) { | ||
if (!~validCallAPIKeys.indexOf(key)) { | ||
validationErrors.push('Invalid [RSAA] key: ' + key); | ||
} | ||
@@ -112,3 +106,4 @@ } | ||
bailout = callAPI.bailout, | ||
fetch = callAPI.fetch; | ||
fetch = callAPI.fetch, | ||
ok = callAPI.ok; | ||
@@ -172,2 +167,8 @@ if (typeof endpoint === 'undefined') { | ||
if (typeof ok !== 'undefined') { | ||
if (typeof ok !== 'function') { | ||
validationErrors.push('[RSAA].ok property must be a function'); | ||
} | ||
} | ||
return validationErrors; | ||
@@ -174,0 +175,0 @@ } |
{ | ||
"name": "redux-api-middleware", | ||
"version": "2.3.0", | ||
"version": "3.0.0-beta.0", | ||
"description": "Redux middleware for calling an API.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
287
README.md
@@ -7,30 +7,77 @@ redux-api-middleware | ||
This middleware receives [*Redux Standard API-calling Actions*](#redux-standard-api-calling-actions) (RSAAs) and dispatches [*Flux Standard Actions*](#flux-standard-actions) (FSAs) to the next middleware. | ||
RSAAs are identified by the presence of an `[RSAA]` property, where [`RSAA`](#rsaa) is a `String` constant defined in, and exported by `redux-api-middleware`. They contain information describing an API call and three different types of FSAs, known as the *request*, *success* and *failure* FSAs. | ||
## Table of contents | ||
1. [Introduction](#introduction) | ||
- [A simple example](#a-simple-example) | ||
- [Breaking Changes in 2.0 Release](#breaking-changes-in-20-release) | ||
2. [Installation](#installation) | ||
3. [Usage](#usage) | ||
- [Defining the API call](#defining-the-api-call) | ||
- [Bailing out](#bailing-out) | ||
- [Lifecycle](#lifecycle) | ||
- [Customizing the dispatched FSAs](#customizing-the-dispatched-fsas) | ||
4. [Reference](#reference) | ||
- [Exports](#exports) | ||
- [Flux Standard Actions](#flux-standard-actions) | ||
- [Redux Standard API-calling Actions](#redux-standard-api-calling-actions) | ||
5. [History](#history) | ||
6. [Tests](#tests) | ||
7. [Upgrading from v1.0.x](#upgrading-from-v10x) | ||
8. [License](License) | ||
9. [Acknowledgements](#acknowledgements) | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
## Introduction | ||
This middleware receives [*Redux Standard API-calling Actions*](#redux-standard-api-calling-actions) (RSAAs) and dispatches [*Flux Standard Actions*](#flux-standard-actions) (FSAs) to the next middleware. | ||
- [Introduction](#introduction) | ||
- [Breaking Changes in 2.0 Release](#breaking-changes-in-20-release) | ||
- [Breaking Changes in 3.0 Release](#breaking-changes-in-30-release) | ||
- [Installation](#installation) | ||
- [configureStore.js](#configurestorejs) | ||
- [app.js](#appjs) | ||
- [Usage](#usage) | ||
- [Defining the API call](#defining-the-api-call) | ||
- [`[RSAA].endpoint`](#rsaaendpoint) | ||
- [`[RSAA].method`](#rsaamethod) | ||
- [`[RSAA].body`](#rsaabody) | ||
- [`[RSAA].headers`](#rsaaheaders) | ||
- [`[RSAA].options`](#rsaaoptions) | ||
- [`[RSAA].credentials`](#rsaacredentials) | ||
- [`[RSAA].fetch`](#rsaafetch) | ||
- [Bailing out](#bailing-out) | ||
- [Lifecycle](#lifecycle) | ||
- [Customizing the dispatched FSAs](#customizing-the-dispatched-fsas) | ||
- [Dispatching Thunks](#dispatching-thunks) | ||
- [Testing](#testing) | ||
- [Reference](#reference) | ||
- [*Request* type descriptors](#request-type-descriptors) | ||
- [*Success* type descriptors](#success-type-descriptors) | ||
- [*Failure* type descriptors](#failure-type-descriptors) | ||
- [Exports](#exports) | ||
- [`RSAA`](#rsaa) | ||
- [`apiMiddleware`](#apimiddleware) | ||
- [`createMiddleware(options)`](#createmiddlewareoptions) | ||
- [`isRSAA(action)`](#isrsaaaction) | ||
- [`validateRSAA(action)`](#validatersaaaction) | ||
- [`isValidRSAA(action)`](#isvalidrsaaaction) | ||
- [`InvalidRSAA`](#invalidrsaa) | ||
- [`InternalError`](#internalerror) | ||
- [`RequestError`](#requesterror) | ||
- [`ApiError`](#apierror) | ||
- [`getJSON(res)`](#getjsonres) | ||
- [Flux Standard Actions](#flux-standard-actions) | ||
- [`type`](#type) | ||
- [`payload`](#payload) | ||
- [`error`](#error) | ||
- [`meta`](#meta) | ||
- [Redux Standard API-calling Actions](#redux-standard-api-calling-actions) | ||
- [`[RSAA]`](#rsaa) | ||
- [`[RSAA].endpoint`](#rsaaendpoint-1) | ||
- [`[RSAA].method`](#rsaamethod-1) | ||
- [`[RSAA].body`](#rsaabody-1) | ||
- [`[RSAA].headers`](#rsaaheaders-1) | ||
- [`[RSAA].options`](#rsaaoptions-1) | ||
- [`[RSAA].credentials`](#rsaacredentials-1) | ||
- [`[RSAA].bailout`](#rsaabailout) | ||
- [`[RSAA].fetch`](#rsaafetch-1) | ||
- [`[RSAA].ok`](#rsaaok) | ||
- [`[RSAA].types`](#rsaatypes) | ||
- [Type descriptors](#type-descriptors) | ||
- [History](#history) | ||
- [Tests](#tests) | ||
- [Upgrading from v1.0.x](#upgrading-from-v10x) | ||
- [Upgrading from v2.0.x](#upgrading-from-v20x) | ||
- [License](#license) | ||
- [Projects using redux-api-middleware](#projects-using-redux-api-middleware) | ||
- [Acknowledgements](#acknowledgements) | ||
RSAAs are identified by the presence of an `[RSAA]` property, where [`RSAA`](#rsaa) is a `String` constant defined in, and exported by `redux-api-middleware`. They contain information describing an API call and three different types of FSAs, known as the *request*, *success* and *failure* FSAs. | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
### A simple example | ||
## Introduction | ||
@@ -93,2 +140,6 @@ The following is a minimal RSAA action: | ||
### Breaking Changes in 3.0 Release | ||
See the [3.0 Release Notes](https://github.com/agraboso/redux-api-middleware/releases/tag/v3.0.0), and [Upgrading from v2.0.x](#upgrading-from-v20x) for details on upgrading. | ||
## Installation | ||
@@ -206,4 +257,7 @@ | ||
Example of modifying a request payload and status: | ||
**Examples:** | ||
<details> | ||
<summary>Modify a response payload and status</summary> | ||
```js | ||
@@ -237,4 +291,6 @@ { | ||
``` | ||
</details> | ||
Example of skipping the request in favor of a cached response: | ||
<details> | ||
<summary>Modify a response status based on response json</summary> | ||
@@ -246,2 +302,23 @@ ```js | ||
fetch: async (...args) => { | ||
const res = await fetch(...args); | ||
const returnRes = res.clone(); // faster then above example with JSON.stringify | ||
const json = await res.json(); // we need json just to check status | ||
returnRes.status = json.error ? 500 : 200, | ||
return returnRes; | ||
} | ||
... | ||
} | ||
} | ||
``` | ||
</details> | ||
<details> | ||
<summary>Skip the request in favor of a cached response</summary> | ||
```js | ||
{ | ||
[RSAA]: { | ||
... | ||
fetch: async (...args) => { | ||
const cached = await getCache('someKey'); | ||
@@ -268,2 +345,3 @@ | ||
``` | ||
</details> | ||
@@ -299,4 +377,4 @@ ### Bailing out | ||
If such an error occurs, a different *request* FSA will be dispatched (*instead* of the one described above). It will contain the following properties: | ||
- `type`: the string constant in the first position of the `[RSAA].types` array; | ||
If such an error occurs, a *failure* FSA will be dispatched containing the following properties: | ||
- `type`: the string constant in the last position of the `[RSAA].types` array; | ||
- `payload`: a [`RequestError`](#requesterror) object containing an error message; | ||
@@ -326,4 +404,118 @@ - `error: true`. | ||
#### *Request* type descriptors | ||
### Dispatching Thunks | ||
You can use `redux-thunk` to compose effects, dispatch custom actions on success/error, and implement other types of complex behavior. | ||
See [the Redux docs on composition](https://github.com/reduxjs/redux-thunk#composition) for more in-depth information, or expand the example below. | ||
<details> | ||
<summary>Example</summary> | ||
```js | ||
export function patchAsyncExampleThunkChainedActionCreator(values) { | ||
return async(dispatch, getState) => { | ||
const actionResponse = await dispatch({ | ||
[RSAA]: { | ||
endpoint: "...", | ||
method: "PATCH", | ||
body: JSON.stringify(values), | ||
headers: { | ||
"Accept": "application/json", | ||
"Content-Type": "application/json", | ||
}, | ||
types: [PATCH, PATCH_SUCCESS, PATCH_FAILED] | ||
} | ||
}); | ||
if (actionResponse.error) { | ||
// the last dispatched action has errored, break out of the promise chain. | ||
throw new Error("Promise flow received action error", actionResponse); | ||
} | ||
// you can EITHER return the above resolved promise (actionResponse) here... | ||
return actionResponse; | ||
// OR resolve another asyncAction here directly and pass the previous received payload value as argument... | ||
return await yourOtherAsyncAction(actionResponse.payload.foo); | ||
}; | ||
} | ||
``` | ||
</details> | ||
### Testing | ||
To test `redux-api-middleware` calls inside our application, we can create a fetch mock in order to simulate the response of the call. The `fetch-mock` and `redux-mock-store`packages can be used for this purpose as shown in the following example: | ||
**actions/user.js** | ||
```javascript | ||
export const USER_REQUEST = '@@user/USER_REQUEST' | ||
export const USER_SUCCESS = '@@user/USER_SUCCESS' | ||
export const USER_FAILURE = '@@user/USER_FAILURE' | ||
export const getUser = () => ({ | ||
[RSAA]: { | ||
endpoint: 'https://hostname/api/users/', | ||
method: 'GET', | ||
headers: { 'Content-Type': 'application/json' }, | ||
types: [ | ||
USER_REQUEST, | ||
USER_SUCCESS, | ||
USER_FAILURE | ||
] | ||
} | ||
}) | ||
``` | ||
**actions/user.test.js** | ||
```javascript | ||
// This is a Jest test, fyi | ||
import configureMockStore from 'redux-mock-store' | ||
import { apiMiddleware } from 'redux-api-middleware' | ||
import thunk from 'redux-thunk' | ||
import fetchMock from 'fetch-mock' | ||
import {getUser} from './user' | ||
const middlewares = [ thunk, apiMiddleware ] | ||
const mockStore = configureMockStore(middlewares) | ||
describe('async user actions', () => { | ||
// If we have several tests in our test suit, we might want to | ||
// reset and restore the mocks after each test to avoid unexpected behaviors | ||
afterEach(() => { | ||
fetchMock.reset() | ||
fetchMock.restore() | ||
}) | ||
it('should dispatch USER_SUCCESS when getUser is called', () => { | ||
// We create a mock store for our test data. | ||
const store = mockStore({}) | ||
const body = { | ||
email: 'EMAIL', | ||
username: 'USERNAME' | ||
} | ||
// We build the mock for the fetch request. | ||
// beware that the url must match the action endpoint. | ||
fetchMock.getOnce(`https://hostname/api/users/`, {body: body, headers: {'content-type': 'application/json'}}) | ||
// We are going to verify the response with the following actions | ||
const expectedActions = [ | ||
{type: actions.USER_REQUEST}, | ||
{type: actions.USER_SUCCESS, payload: body} | ||
] | ||
return store.dispatch(actions.getUser()).then(() => { | ||
// Verify that all the actions in the store are the expected ones | ||
expect(store.getActions()).toEqual(expectedActions) | ||
}) | ||
}) | ||
}) | ||
``` | ||
## Reference | ||
### *Request* type descriptors | ||
`payload` and `meta` functions will be passed the RSAA action itself and the state of your Redux store. | ||
@@ -389,3 +581,3 @@ | ||
#### *Success* type descriptors | ||
### *Success* type descriptors | ||
@@ -455,3 +647,3 @@ `payload` and `meta` functions will be passed the RSAA action itself, the state of your Redux store, and the raw server response. | ||
#### *Failure* type descriptors | ||
### *Failure* type descriptors | ||
@@ -489,2 +681,3 @@ `payload` and `meta` functions will be passed the RSAA action itself, the state of your Redux store, and the raw server response — exactly as for *success* type descriptors. The `error` property of dispatched *failure* FSAs will always be set to `true`. | ||
``` | ||
By default, *failure* FSAs will not contain a `meta` property, while their `payload` property will be evaluated from | ||
@@ -498,4 +691,5 @@ ```js | ||
## Reference | ||
Note that *failure* FSAs dispatched due to fetch errors will not have a `res` argument into `meta` or `payload`. The `res` parameter will exist for completed requests that have resulted in errors, but not for failed requests. | ||
### Exports | ||
@@ -513,2 +707,11 @@ | ||
#### `createMiddleware(options)` | ||
A function that creates an `apiMiddleware` with custom options. | ||
The following `options` properties are used: | ||
- `fetch` - provide a `fetch` API compatible function here to use instead of the default `window.fetch` | ||
- `ok` - provide a function here to use as a status check in the RSAA flow instead of `(res) => res.ok` | ||
#### `isRSAA(action)` | ||
@@ -626,5 +829,5 @@ | ||
A *Redux Standard API-calling Action* MUST NOT | ||
A *Redux Standard API-calling Action* MAY | ||
- include properties other than `[RSAA]`. | ||
- include properties other than `[RSAA]` (but will be ignored by redux-api-middleware). | ||
@@ -647,7 +850,8 @@ #### `[RSAA]` | ||
- have a `bailout` property, | ||
- have a `fetch` property. | ||
- have a `fetch` property, | ||
- have an `ok` property. | ||
The `[RSAA]` property MUST NOT | ||
- include properties other than `endpoint`, `method`, `types`, `body`, `headers`, `options`, `credentials`, `bailout` and `fetch`. | ||
- include properties other than `endpoint`, `method`, `types`, `body`, `headers`, `options`, `credentials`, `bailout`, `fetch` and `ok`. | ||
@@ -686,4 +890,8 @@ #### `[RSAA].endpoint` | ||
The optional `[RSAA].fetch` property MUST be a function. | ||
The optional `[RSAA].fetch` property MUST be a function that conforms to the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). | ||
#### `[RSAA].ok` | ||
The optional `[RSAA].ok` property MUST be a function that accepts a response object and returns a boolean indicating if the request is a success or failure | ||
#### `[RSAA].types` | ||
@@ -724,4 +932,11 @@ | ||
- A new `options` config is added to pass your `fetch` implementation extra options other than `method`, `headers`, `body` and `credentials` | ||
- `apiMiddleware` no longer returns a promise on actions without [RSAA] | ||
- `apiMiddleware` no longer returns a promise on actions without [RSAA] | ||
## Upgrading from v2.0.x | ||
- The `CALL_API` alias has been removed | ||
- Error handling around failed fetches has been updated (#175) | ||
- Previously, a failed `fetch` would dispatch a `REQUEST` FSA followed by another `REQUEST` FSA with an error flag | ||
- Now, a failed `fetch` will dispatch a `REQUEST` FSA followed by a `FAILURE` FSA | ||
## License | ||
@@ -728,0 +943,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
69806
763
938
1