redux-source
Using GraphQL schema and query language to access any data source (eg. RESTful APIs) and automatically generate reducers, actions and normalized state
npm install --save redux-source
For Immutable.js store: redux-source-immutable
Examples
Get Started
For example, suppose we have a CRUD (create, read, update, delete) API for managing shops
Create A Data Source
createSource
Create a data source instance
// shopManageApp/apis/shops/index.js;// for Immutable.js store:// import { createSource } from 'redux-source-immutable';;; const source = ;
TIPS
- Add raw-loader to your webpack.config.js for importing .gql file
{ test: /\.(txt|gql)$/, use: 'raw-loader', },
Schema
# shopManageApp/apis/shops/schema/index.gqltype Shop { id: ID! name: String address: String deliveryEnabled: Boolean! services: [Service!] orders: [JSON!] position: Position}type Service { id: ID! name: String! price: Float!}type Query { shops: [Shop!]}type Mutation { updateShop(shopId: String!, shopData: JSON!): [Shop!] deleteShop(shopId: String!): [Shop!]}
TIPS
- Syntax highlighting for GraphQL schema language and query language
- GraphQL schema language
JSON
type is built-in and defined by graphql-type-json ("field type for object with dynamic keys")
- Other built-in scalar types:
Date
,Time
,DateTime
(graphql-iso-date)- You can define your own custom scalar
- If you want to automatically normalize (see below) the data of a field, its field name must be plural (for the above example, the field names for
Service
type andShop
type areservices
andshops
) or 'xxxx_$list' when its type is a List and must be singular or uncountable when its type is not a List.
Resolvers
// shopManageApp/apis/shops/resolvers/index.js; const resolvers = Query: Mutation: Shop ; ;
TIPS
createSource
'sschema
andresolvers
options are the same as graphql-tools'smakeExecutableSchema
- For more complex query or mutation operations, you can use composition libraries such as graphql-resolvers.
- Examples of how to modularize the above code: react-redux-restapi-app/common/apis
Create A Data Source Query
Queries
Use GraphQL query language to automatically generate reducers, actions and normalized state:
// shopManageApp/ducks/shops.js;; const shopsSource = ; // use `shopsSource` to generate reducer function and action creators
TIPS
- GraphQL query language
__config__
is an extended syntax supported by redux-source
combineResult
is used to indicate how the autogenerated reducer to change the state, its value can bemerge
(default),replace
, orcrop
- Examples: CRUD API demo in react-source-sample
Reducers, Actions and States
The output of source query:
console// {// 'REDUX_SOURCE/FETCH_SHOPS': asyncActionCreator,// 'REDUX_SOURCE/FETCH_SHOPS_PENDING': actionCreator,// 'REDUX_SOURCE/FETCH_SHOPS_SUCCESS': actionCreator,// 'REDUX_SOURCE/FETCH_SHOPS_ERROR': actionCreator,// 'REDUX_SOURCE/ADD_SHOP': asyncActionCreator,// 'REDUX_SOURCE/ADD_SHOP_PENDING': actionCreator,// 'REDUX_SOURCE/ADD_SHOP_SUCCESS': actionCreator,// 'REDUX_SOURCE/ADD_SHOP_ERROR': actionCreator,// 'REDUX_SOURCE/UPDATE_SHOP': asyncActionCreator,// 'REDUX_SOURCE/UPDATE_SHOP_PENDING': actionCreator,// 'REDUX_SOURCE/UPDATE_SHOP_ERROR': actionCreator,// 'REDUX_SOURCE/UPDATE_SHOP_SUCCESS': actionCreator,// 'REDUX_SOURCE/DELETE_SHOP': asyncActionCreator,// 'REDUX_SOURCE/DELETE_SHOP_PENDING': actionCreator,// 'REDUX_SOURCE/DELETE_SHOP_ERROR': actionCreator,// 'REDUX_SOURCE/DELETE_SHOP_SUCCESS': actionCreator,// } console// {// 'REDUX_SOURCE/FETCH_SHOPS_PENDING': reducer,// 'REDUX_SOURCE/FETCH_SHOPS_SUCCESS': reducer,// 'REDUX_SOURCE/FETCH_SHOPS_ERROR': reducer,// 'REDUX_SOURCE/ADD_SHOP_PENDING': reducer,// 'REDUX_SOURCE/ADD_SHOP_SUCCESS': reducer,// 'REDUX_SOURCE/ADD_SHOP_ERROR': reducer,// 'REDUX_SOURCE/UPDATE_SHOP_PENDING': reducer,// 'REDUX_SOURCE/UPDATE_SHOP_ERROR': reducer,// 'REDUX_SOURCE/UPDATE_SHOP_SUCCESS': reducer,// 'REDUX_SOURCE/DELETE_SHOP_PENDING': reducer,// 'REDUX_SOURCE/DELETE_SHOP_ERROR': reducer,// 'REDUX_SOURCE/DELETE_SHOP_SUCCESS': reducer,// } console// {// source: {// result: {},// entities: {},// isPending: false,// errors: [],// }// }
TIPS
- How to compile GraphQL queries at the build time
- How to use the output of source query (like
shopsSource
) with redux-cube
- New docs coming soon! (based on the new
createCube
API and webcube's SSR feature)- Example: redux-source-sample
How will the action creators (shopsSource.actions
) and reducers (shopsSource.reducerMap
) change the state slice (shopsSource.initialState
):
shopsSourceactionsreduxSource// state slice:// {// source: {// result: {// shops: ['shop-id-0001', 'shop-id-0002'],// },// entities: {// shop: {// 'shop-id-0001': {// id: 'shop-id-0001',// name: 'Shop A',// services: ['service-id-0001', 'serivce-id-0001'],// position: {// latitude: '...',// longitude: '...',// },// ...// },// 'shop-id-0002': {// id: 'shop-id-0002',// name: 'Shop B',// services: ['shop-id-0002', 'shop-id-0003'],// position: {// latitude: '...',// longitude: '...',// },// ...// },// },// service: {// 'service-id-0001': {// name: 'Service A',// ...// },// 'service-id-0002': {// ...// },// 'service-id-0003': {// ...// },// },// },// isPending: false,// errors: [],// }// } shopsSourceactionsreduxSource// state slice:// {// source: {// result: {// shops: ['shop-id-0001', 'shop-id-0002', 'shop-id-0003'],// },// entities: {// shop: {// 'shop-id-0001': {// ...// },// 'shop-id-0002': {// ...// },// 'shop-id-0003': {// id: 'shop-id-0003',// name: 'Shop C',// ...// },// },// service: {// 'service-id-0001': {// ...// },// 'service-id-0002': {// ...// },// 'service-id-0003': {// ...// },// 'service-id-0004': {// name: 'Service D',// ...// },// },// },// isPending: false,// errors: [],// }// } shopsSourceactionsreduxSource// state slice:// {// source: {// result: {// shops: ['shop-id-0001', 'shop-id-0002', 'shop-id-0003'],// },// entities: {// shop: {// 'shop-id-0001': {// id: 'shop-id-0001',// name: 'Shop A (modified)',// ...// },// 'shop-id-0002': {// ...// },// 'shop-id-0003': {// ...// },// },// service: {// ...// },// },// isPending: false,// errors: [],// }// } shopsSourceactionsreduxSource// state slice:// {// source: {// result: {// shops: ['shop-id-0001', 'shop-id-0003'],// },// entities: {// shop: {// 'shop-id-0001': {// ...// },// 'shop-id-0002': {// ...// },// 'shop-id-0003': {// ...// },// },// service: {// 'service-id-0001': {// ...// },// 'service-id-0002': {// ...// },// 'service-id-0003': {// ...// },// 'service-id-0004': {// ...// },// },// },// isPending: false,// errors: [],// }// }
TIPS
- redux-source can automatically generate the schema definition for normalizr and automatically normalize the output of GraphQL resolvers (equal to
shopsSource.normalize(outputOfAllResolvers)
)- The
position
field in the result is not normalized because it does not contain anid
field
- The name of the
id
field can be customized byidAttribute
option (see below)
How To Customize
const shopsSource = console// {// 'SHOPS_NAMESPACE|FETCH_SHOPS': asyncActionCreator,// 'SHOPS_NAMESPACE|FETCH_SHOPS_PENDING': actionCreator,// 'SHOPS_NAMESPACE|FETCH_SHOPS_SUCCESS': actionCreator,// 'SHOPS_NAMESPACE|FETCH_SHOPS_ERROR': actionCreator,// ...// 'SHOPS_NAMESPACE|DELETE_SHOP_SUCCESS': actionCreator,// } console// {// shopsSource: {// result: {},// entities: {},// isPending: false,// errors: [],// }// }
Higher-order Components
Connect To React Components
New docs coming soon! (based on the new
createCube
API and webcube's SSR feature)
Notification
Block UI
See redux-source-with-block-ui
Immutable.js Store
Examples for Immutable.js store: app/react-redux-restapi-app/immutableJsStore