JSPM

  • Created
  • Published
  • Downloads 1326
  • Score
    100M100P100Q102404F

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

Build Status NPM version Coverage Status

Inspired by Redux-rest and is recommended to work with Redux.

Install

with npm

npm install redux-api --save

with 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/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) {  //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 true this endpoint doesn't create reducer and doesn't emit redux-api actions. All data broadcasting by actions from broadcast list. If broadcast list is empty virtual value will be false anyway.

    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 use adaptersFetch adapter for rest backend using fetch API for rest isomorphic-fetch
    @param isServer - redux api is isomorphic compatible see examples/isomorphic By default isServer===false for clien-size mode. If isServer===true redux-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 twice

reducerName

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 backend

index.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

Releases Changelog