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/entry
where 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
delimiter
andarrayFormat
are 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
true
this endpoint doesn't create reducer and doesn't emit redux-api actions. All data broadcasting by actions frombroadcast
list. - 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 body
In 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 withhelpers
definitions. - 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
reduxApi
with custom properties - Param key - name of property
- Param value - value of property
- Description: backend adapter. In current example we use
adaptersFetch
adapter for rest backend usingfetch
API 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===false
for client-size mode. Ifserver===true
redux-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/get
will be/api/get
and 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
Error
is 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:
reduxApi
initializer returns not initialized object. You need to callinit
for initialize it. - Type: Function
- Param adapter - backend adapter. In current example we use
adaptersFetch
adapter for rest backend usingfetch
API for rest isomorphic-fetch - Param isServer - redux api is isomorphic compatible see examples/isomorphic By default
isServer===false
for client-size mode. IfisServer===true
redux-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/get
will be/api/get
and 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 === true
request 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
abort
and 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
mutation
equalsync
, it reset onlysync
flag 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=2
Each 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
sync
flag will be set totrue
after the first dispatched request is handled. - The
syncing
flag is set totrue
while a dispatched sync request is being handled, but only when thesync
flag isfalse
. This can only happen once when not manually resetting thesync
flag during execution, since thesync
flag is set totrue
after the first dispatched request is handled. - The
loading
flag is set totrue
while a dispatched request is being handled. After the dispatched request is handled, its value will be reset tofalse
. - The
error
property contains the response error of a dispatched request after it is handled. - The
data
property contains the response data of a dispatched request after it is handled.