Package Exports
- redux-api
- redux-api/lib/adapters/fetch
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (redux-api) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Redux-api
Flux REST API for redux infrastructure
Inspired by Redux-rest and is recommended to work with Redux.
Install
with npm
npm install redux-api --savewith bower
bower install redux-api --save=======
Examples
examples/isomorphic - React + Redux + React-Router + Redux-api with webpack and express + github api
Documentation
import reduxApi, {transformers} from "redux-api";reduxApi(options)
- @description create endpoint
- @param options - configuration 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) { //it's default value
return {};
}
}
}- @param options.{endpoint}.url - endpoint for rest api
type: String
- @param options.{endpoint}.transformer - response transformer
type: Function
default: transformers.object
example: It's a good idea to write custom transformer
for example you have responce
{ "title": "Hello", "message": "World" }Custom transformer function customTransformer(data) {
data || (data = {});
return { title: (data.title || ""), message: (data.message || "")};
}- @param options.{endpoint}.options - Options for rest-api backend.
function(url, options)type: Object | Funtions
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"
}
};
}- @param options.{endpoint}.broadcast - 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;
}
}- @param options.{endpoint}.virtual - if virtual is
truethis endpoint doesn't create reducer and doesn't emit redux-api actions. All data broadcasting by actions frombroadcastlist. Ifbroadcastlist is emptyvirtualvalue will befalseanyway.type: Array default: false
reduxApi object
reduxApi initializer returns non initialized object. You need to call init for initilize it.
import "isomorphic-fetch";
import reduxApi from "redux-api";
import adapterFetch from "redux-api/adapters/fetch";
const rest = reduxApi({
... //config
});
rest.init(adapterFetch(fetch), false);- reduxApi().init(adapter, isServer)
type: Function - initialize reduxApi object
@param adapter - backend adapter. In curent example we useadaptersFetchadapter for rest backend usingfetchAPI for rest isomorphic-fetch
@param isServer - redux api is isomorphic compatible see examples/isomorphic By defaultisServer===falsefor clien-size mode. IfisServer===trueredux-api works in server-size mode.
actions
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
//also available helper methods
dispatch(rest.actions.entries.reset()) // set initialState to store
dispatch(rest.actions.entries.sync()) // this mathod save you from twice requests
// flag `sync`. if `sync===true` requst
// wouldnt execute.
// In server-side mode calls twicereducerName
Sometimes though, you might want named actions that go back to the same reducer. For 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, Redux@1.0.0-rc, but it's pretty simple to migrate this code to Redux@v0.12.0
###Example rest.js
import "isomorphic-fetch";
import reduxApi, {transformers} from "redux-api";
import adapterFetch from "redux-api/adapters/fetch";
export default reduxApi({
// simple edpoint description
entry: `/api/v1/entry/:id`,
// complex endpoint description
regions: {
url: `/api/v1/regions`,
// reimplement default `transformers.object`
transformer: transformers.array,
// base endpoint options `fetch(url, options)`
options: {
header: {
"Accept": "application/json"
}
}
}
}).init(adapterFetch(fetch)); // it's nessasary to point using rest backendindex.jsx
import React, {PropTypes} from "react";
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";
import { Provider, connect } from "react-redux";
import rest from "./rest"; //our redux-rest object
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(rest.reducers);
const store = createStoreWithMiddleware(reducer);
@connect((state)=> ({ entry: state.entry, regions: state.regions }))
class Application {
static propTypes = {
entry: PropTypes.object.isRequired,
regions: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired
}
componentDidMount() {
const {dispatch} = this.props;
// fetch `/api/v1/regions
dispatch(rest.actions.regions.sync());
//specify id for GET: /api/v1/entry/1
dispatch(rest.actions.entry({id: 1}));
}
render() {
const {entry, regions} = this.props;
const Regions = regions.data.map((item)=> <p>{ item.name }</p>)
return (
<div>
Loading regions: { regions.loading }
<Regions/>
Loading entry: {entry.loading}
<div>{{ entry.data.text }}</div>
</div>
);
}
}
React.render(
<Provider store={store}>
{ ()=> <Application /> }
</Provider>,
document.getElementById("content")
);Store state schema
const rest = reduxApi({
user: "/user/1"
});// initialState
{
user: {
sync: false, // State was update once
syncing: false, // State syncing is in progress
loading: false, // State updating is in progress
error: null, // responce error
data: [] // responce data
}
}Url schema
/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