import reduxApi, {transformers} from "redux-api";- Description: create endpoint
- Param options - configuration of rest-api endpoints
- Type: Object
- Default: {}
- Example:
Simple endpoint definition
GET /api/v1/entrywhere response is Object
{
entry: "/api/v1/entry",
}
// equivalent
{
entry: {
url: "/api/v1/entry"
}
}
// equivalent
{
entry: {
url: "/api/v1/entry",
transformer: transformers.object, //it's default value
options: {} //it's default value
}
}
// equivalent
{
entry: {
url: "/api/v1/entry",
transformer: transformers.object, //it's default value
options: function(url, params, getState) { //it's default value
return {};
}
}
}Param baseConfig - additional base configuration Param baseConfig.prefix - custom prefix for ACTIONS if you use more then 1 restApi instance Type: String Default: ""
- Description: url endpoint
- Type: String
- Example:
{
entry: {
url: "/api/v1/entry"
}
}- Description: options for transforming urls
- Type: Object
- Example: Keys
delimiterandarrayFormatare passed on to qs#parse and qs#stringify:
{
entry: {
url: "/api/v1/entry",
urlOptions: {
delimiter: ";",
arrayFormat: "brackets"
}
}
}To pass different options to #parse and #stringify, use the qsParseOptions and qsStringifyOptions keys:
{
entry: {
url: "/api/v1/entry?a[]=5,a[]=6",
urlOptions: {
arrayFormat: "brackets",
qsParseOptions: {
delimiter: /[,;]/
},
qsStringifyOptions: {
delimiter: ";"
}
}
}
}This would re-encode the url to /api/v1/entry?a[]=5;a[]=6.
- Description: function for rest response transformation
- Type: Function
- Default: transformers.object
- Example: It's a good idea to write custom transformer for example you have response
{ "title": "Hello", "message": "World" }Custom transformer
function customTransformer(data, prevData, action) {
data || (data = {});
return { title: (data.title || ""), message: (data.message || "")};
}- Description: options for rest-api backend.
function(url, options) - Type: Object | Functions
- Default: null
- Example: if you use isomorphic-fetch backend
options: {
method: "post",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
}
// equivalent
options: function() {
return {
method: "post",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
};
}- Description: cache response. By default cache is turn off. If cache = true - this means that cache is permanent. Also cache can be object. see example
- Type Boolean, Object, null
- Default: null
- Example:
{
permanent: {
url: "/api/v1/permanent",
cache: true
},
expires1: {
url: "/api/v1/expires/1",
cache: { expire: 360 }, // 360 seconds
},
expires2: {
url: "/api/v1/expires/2",
cache: {
expire: new Date("...."), // use concrete Date
id(params, params) {
// here you can overwrite cache id for request
return `you custom id for request`;
}
}
}
}- @deprecated
- Description: list of actions which would emit after data fetching.
- Type: Array
- Default: null
- Example:
import {ACTION_ENTRY_UPDATE} from "./constants";
....
entry: {
url: "/api/v1/entry",
broadcast: [ ACTION_ENTRY_UPDATE ]
}
// in your redux reducer
function (state, action) {
switch (action.type) {
case ACTION_ENTRY_UPDATE:
return {
...state,
data: action.data // fetching data
};
default:
return state;
}
}- Description: Define your custom reducer to catch other events and modify state of current entry ATTENTION: custom reducer can't catch default events for current entry.
- Type: Function
- Default: null
- Example:
const rest = reduxApi({
hello: "/api/hello",
item: {
url: "/api/item",
reducer(state, action) {
/*
ATTENTION: this.events.item.actionSuccess and other default redux-api events never catch there
*/
// context has instance
if (action.type === "MY_CUSTOM_EVENT") {
return { ...state, value: action.value };
} else if (action.type === this.events.hello.actionSuccess) {
return { ...state, value: action.value };
} else {
return state;
}
}
}
});- Description: if virtual is
truethis endpoint doesn't create reducer and doesn't emit redux-api actions. All data broadcasting by actions frombroadcastlist. - Type: Array
- Default: false
- Example: It usefull, for example, when you need to manipulate list of items. But you don't want to persist information about each manipulation, you want to save it in list.
const rest = reduxApi({
items: "/api/items",
item: {
url: "/api/item/:id",
virtual: true, //reducer in this case doesn't generate
postfetch: [
function({ dispatch, actions }) {
dispatch(actions.items()); // update list of items after modify any item
}
]
}
});In this case you global state is look like this:
{ items: [ ... ] }- Description: you can organize chain of calling events before the current endpoint will be executed
- Type: Array
- Default: null
- Example:
{
user: "/user/info",
profile: "/user/:name",
changeName: {
url: "/user/changename",
prefetch: [
function({actions, dispatch, getState}, cb) {
const {user: {data: {name}}} = getState();
name ? cb() : dispatch(actions.user(cb));
},
function({actions, dispatch, getState}, cb) {
const {user: {data: {name}}, profile: {data: {uuid}}} = getState();
uuid ? cb() : dispatch(actions.profile({name}, cb));
}
],
options: function(url, params, getState) {
const {profile: {data: {uuid}}} = getState();
return { ...params, body: { ...params.body, uuid }};
}
},
friends: {
url: "/user/:name/friends",
prefetch: [
function({actions, dispatch, getState, requestOptions}, cb) {
const {profile: {data: {uuid}}} = getState();
const {pathvars: {name}} = requestOptions;
uuid ? cb() : dispatch(actions.profile({name}, cb));
}
],
options: function(url, params, getState) {
const {profile: {data: {uuid}}} = getState();
return { ...params, body: { ...params.body, uuid }};
}
}
}- Description: you can organize chain of calling events after the current endpoint will be successful executed
- Type: Array
- Default: null
- Example:
{
user: "/user/info",
logout: {
url: "/user/logout",
postfetch: [
function({data, actions, dispatch, getState, request}) {
dispatch(actions.user.reset());
}
]
}
}-
Param data - response data
type: Object
-
Param callback - you need to execute this callback function to finish data validation
type: Function
-
Example:
{
test: {
url: "/api/test",
validation: (data, cb) {
// check data format
let error;
if (data instanceOf Array) {
error = "Data must be array";
}
cb(error);
}
}
}- Description: Sometimes though, you might want named actions that go back to the same reducer. For example:
- Type: String
- Example:
import reduxApi, {transformers} from "redux-api";
const rest = reduxApi({
getUser: {
reducerName: "user"
url: "/user/1", // return a user object
}
updateUser: {
reducerName: "user"
url: "/user/1/update",
options: {
method: "post"
}
}
});
const {actions} = rest;
// In component with redux support (see example section)
const {dispatch} = this.props;
dispatch(rest.actions.getUser()); // GET "/api/v1/entry"
dispatch(rest.actions.updateUser({}, {
body: JSON.stringify({ name: "Hubot", login: "hubot"})
})); // POST "/api/v1/entry/1" with bodyIn the above example, both getUser, and updateUser update the same user reducer as they share the same reducerName For example used es7 javascript
- Description: you can create custom helper function which work with this rest endpoint but with different parameters.
- Type: Object
- Example:
{
logger: "/api/logger",
test: {
url: "/api/test/:name/:id",
helpers: {
get(id, name) {
return [{id, name}], {}]
},
post(id, name, data) {
const {uuid} = this.getState().test;
const urlparams = {id, name};
const params = {body: {uuid, data}};
return [urlparams, params];
},
// complicated async logic
async() {
const {dispatch} = this;
return (cb)=> {
dispatch(rest.actions.logger((err)=> {
const args = [{id: 1, name: "admin"}];
cb(err, args);
}));
};
}
}
}
}
// using helpers
rest.actions.test.get(1, "admin");
// with callback
rest.actions.test.post(1, "admin", {msg: "Hello"}, (err)=> {
// end of action
});
rest.actions.test.async();- Description: autogenerate
helpers("get", "post", "put", "delete", "patch") for selected endpoint. Also you can overwrite autogenerate action withhelpersdefinitions. - Type: Boolean
- Default: false
- Example:
{
test: {
url: "/test/:id",
crud: true
}
}
//using
rest.actions.test.get({ id: 1})
rest.actions.test.post({ id: 1}, { body: "data" }, (err, data)=> {
//code
});
rest.actions.test.put({ id: 1}, { body: "data" })
rest.actions.test.delete({ id: 1 });- Description: initialize
reduxApiwith custom properties - Param key - name of property
- Param value - value of property
- Description: backend adapter. In current example we use
adaptersFetchadapter for rest backend usingfetchAPI for rest isomorphic-fetch - Example:
import adapterFetch from "redux-api/lib/adapters/fetch";
const rest = reduxApi({...});
rest.use("fetch", adapterFetch(fetch));- Description: redux api is isomorphic compatible see examples/isomorphic By default
server===falsefor client-size mode. Ifserver===trueredux-api works in server-size mode. - Default false
const rest = reduxApi({...});
rest.use("server", true);- Description: root url for every endpoint. very usefull for isomorphic(universal) app. For client-side use default rootUrl, and for backend use http://localhost:80 for example. For client-side for request
/api/getwill be/api/getand for backend will behttp://localhost:80/api/get - Type: String | Functions
- Example:
const rest = reduxApi({...});
rest.use("rootUrl", "http://localhost:3000");Or a function
const rest = reduxApi({...});
rest.use("rootUrl", function(url, params, getState) {
return getState().config.rootUrl;
});- Description: Apply add options for each rest call.
- Type: String | Functions
- Example:
const rest = reduxApi({...});
rest.use("options", function() {
const headers = {
'User-Agent': 'foodsoft-shop', // @todo add version
'Accept': 'application/json'
};
return { headers: headers };
});Or a function
const rest = reduxApi({...});
rest.use("options", function(url, params getState) {
return {
headers: {
'X-Token': getState().user.accessToken
}
};
});- Description: if you use middleware different from redux-thunk you can realize custom behaviour for argument parser.
- Example:
// Custom middleware
const cutsomThunkMiddleware = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action({ dispatch, getState });
}
return next(action);
};
// middlewareParser
reduxApi({ ... }).use("middlewareParser",
({ dispatch, getState })=> {
return { getState, dispatch };
});- Description: catch all http response from each redux-api endpoint. First argument is
Erroris response fail, second argument data from success response. It can be used for logging, error handling or data transformation. - Example:
reduxApi({ ... }).use("responseHandler",
(err, data)=>
err ? console.log("ERROR", err) : console.log("SUCCESS", data));reduxApi({ ... }).use("responseHandler",
(err, data)=> {
if (err.message === 'Not allowed') {
throw new NotAllowedError();
} else {
return data;
}
});- @deprecated
- Description:
reduxApiinitializer returns not initialized object. You need to callinitfor initialize it. - Type: Function
- Param adapter - backend adapter. In current example we use
adaptersFetchadapter for rest backend usingfetchAPI for rest isomorphic-fetch - Param isServer - redux api is isomorphic compatible see examples/isomorphic By default
isServer===falsefor client-size mode. IfisServer===trueredux-api works in server-size mode. - Param rootUrl - root url for every endpoint. very usefull for isomorphic(universal) app. For client-side use default rootUrl, and for backend use http://localhost:80 for example. For client-side for request
/api/getwill be/api/getand for backend will behttp://localhost:80/api/get. - Example:
import "isomorphic-fetch";
import reduxApi from "redux-api";
import adapterFetch from "redux-api/lib/adapters/fetch";
const rest = reduxApi({
... //config
});
rest.init(adapterFetch(fetch), false, "http://localhost:3000");- Description: list of redux actions for rest manipulations
- Type: Object
- Example:
const rest = reduxApi({
entries: "/api/v1/entry",
entry: {
url: "/api/v1/entry/:id",
options: {
method: "post"
}
}
});
// ....
const {actions} = rest;
/*
initialState for store
store = {
entries: {
loading: false, // request finish flag
sync: false, // data has loaded minimum once
data: {} // data
},
entry: { loading: false, sync: false, data: {} },
}
*/
// In component with redux support (see example section)
const {dispatch} = this.props;
dispatch(rest.actions.entries()); // GET "/api/v1/entry"
dispatch(rest.actions.entry({id: 1}, {
body: JSON.stringify({ name: "Hubot", login: "hubot"
}})); // POST "/api/v1/entry/1" with body
dispatch(rest.actions.entries.reset());
dispatch(rest.actions.entries.sync());- Description: this method save you from twice requests flag
sync. ifsync === truerequest wouldn't execute. In server-side mode calls twice - Param urlparams - update url according Url schema
- Param params - add additional params to rest request
- Param callback - callback function when action ends
- Type: Function
- Example:
import {actions} from "./rest";
function onEnter(state, replaceState, callback) {
dispatch(rest.actions.entries.sync(callback));
}- Description: abort loading request
- Type: null
- Example:
import {actions} from "./rest";
dispatch(actions.entries({ id: 1 }))
actions.entries.abort() // abort previous request
dispatch(actions.entries({ id: 2 }))- Description: abort previous request if it performs and after that perform new request. This method combines
abortand direct call action methods. - Type: Function
- Example:
import {actions} from "./rest";
dispatch(actions.entries({ id: 1 }))
dispatch(actions.entries.force({ id: 2 }))
- Description: Reset state of current reducer and application abort request if it processed.
- Type: Function
- Param mutation: if
mutationequalsync, it reset onlysyncflag in store. - Example:
import {actions} from "./rest";
function onLeave(state, replaceState, cb) {
dispatch(actions.entries.reset(cb));
}- Description: Pure xhr request is without sending events or catching reducers.
- Type: Function
- Example:
import {actions} from "./rest";
actions.entries.request().then((data)=> {
....
});/api/v1/user/:id
rest.actions.user({id: 1}) // /api/v1/user/1/api/v1/user/(:id)
rest.actions.user({id: 1}) // /api/v1/user/1/api/v1/user/(:id)
rest.actions.user({id: 1, test: 2}) // /api/v1/user/1?test=2Each endpoint in redux-api infrastructure has own collection of methods.
- actionFetch - emits when endpoint's call is started
- actionSuccess - emits when endpoint's call finishes with success result
- actionFail - emits when endpoint's call finishes with error result
- actionReset - emits when reset action was called
you can get access for anyone using next accessible properties
rest.events.user.actionFetch // actionSuccess actionFail actionReset
....It's very useful when you need to update external reducer using information from redux-api.
const initialState = { access: false };
function accessReducer(state=initialState, action) {
switch (action.type) {
case UPDATE_ACCESS: // manual update
return { ...state, access: action.access };
case rest.events.user.actionSuccess: // user has own information about access
return { ...state, access: action.data.access };
default:
state;
}
}- Description: helps to organize chain call of actions
- Example:
import reduxApi, { async } from "redux-api";
const rest = reduxApi({
test: "/api/test",
test2: "/api/test2",
test3: "/api/test3"
});
async(dispatch,
(cb)=> rest.actions.test(cb),
rest.actions.test2
).then((data)=> async(rest.actions.test3));import reduxApi from "redux-api";
const rest = reduxApi({
user: "/user/1"
});In the above example, an endpoint for a user object is created. The corresponding initial store state for this object is the following:
// initialState
{
user: {
sync: false, // State was update once
syncing: false, // State syncing is in progress
loading: false, // State updating is in progress
error: null, // response error
data: [] // response data
}
}- The
syncflag will be set totrueafter the first dispatched request is handled. - The
syncingflag is set totruewhile a dispatched sync request is being handled, but only when thesyncflag isfalse. This can only happen once when not manually resetting thesyncflag during execution, since thesyncflag is set totrueafter the first dispatched request is handled. - The
loadingflag is set totruewhile a dispatched request is being handled. After the dispatched request is handled, its value will be reset tofalse. - The
errorproperty contains the response error of a dispatched request after it is handled. - The
dataproperty contains the response data of a dispatched request after it is handled.