redux-crud-async will create CRUD async actions and states automatically, just for you. The configuration is minimalist.

It currently uses axios or sails.io websocket (custom socket.io) for XHR. It allows you to use a REST API with authentication with a Bearer Token. In a near future, I will implement the possibility to create random actions like sign_in , sign_out or tranformThisLeadInGold Redux-crud-async is built against 150+ tests. Unfortunately this does not mean that redux-crud-async is bug free, please send issues if you find one !

Table of Contents

Setup

NPM

you can find it on NPM

npm i -S redux-crud-async

ActionTypes

var reduxCrudAsync = require ( ' redux-crud-async ' ) var crud = new reduxCrudAsync ( ) module . exports = { ... crud . primaryActionTypesFor ( ' channel ' ) , ... crud . primaryActionTypesFor ( ' tag ' ) , ... crud . associationActionTypesFor ( ' channel ' , ' tag ' ) }

Actions

var reduxCrudAsync = require ( ' redux-crud-async ' ) var hostConfig = { host : ' https://my-api.com ' , prefix : ' v1 ' , pluralizeModels : false , } var crud = new reduxCrudAsync ( hostConfig ) module . exports = { ... crud . primaryActionsFor ( ' channel ' ) , ... crud . primaryActionsFor ( ' tag ' ) , ... crud . associationActionsFor ( ' channel ' , ' tag ' ) }

Reducers

var reduxCrudAsync = require ( ' redux-crud-async ' ) var crud = new reduxCrudAsync ( ) module . exports = { ... crud . primaryReducerFor ( ' user ' ) , ... crud . primaryReducerFor ( ' pet ' ) , ... crud . associationReducerFor ( ' user ' , ' pet ' ) }

EXEMPLE USAGE Click here to see how much it is easy to use this module

Conventions

General

It might be obvious but all models returned by your database need to have an unique id.

Routes

This module is built to work with sails.js blueprints routes using sails-rest-api conventions. It differentiate singular and plural model name : findUser !== findUsers IMPORTANT ! As sails.js, this module uses pluralize module which pluralize words grammaticaly.

some exemples : channel -> channels person -> people coach -> coaches

By default, all your routes will be pluralized, Person model will have the following :

state : person - a single person people - all your "persons"

actions : findPerson -> will hit GET /people/:id findPeople -> will hit GET /people

You can unpluralize your urls by setting it in the config

Authentication

redux-crud-async uses a Bearer Token to authenticate requests. It is store in window.localStorage . Every request which need authentication is sent with the token in the header following this convention :

Token is set in Authorization header as :

{ headers : { Authorization : ' Bearer ' + JWT_Token_from_localStorage } }

Socket

We use the io.socket.request to communicate to the server. Make sure that your server can use it if you don't use sails.js on server side.

Configuration

Config file

Name Type Default Description host String null Your API host - must be defined prefix String null A prefix for all your routes. Don't add the slash / on the prefix it will be automatically added. pluralizeModels Boolean true Use pluralized model names in url. This has no affect on action names. socket Boolean false Use socket.io for actions localStorageName String "JWT" The key for retrieving your JWT Token from window.localStorage headerContent String "Bearer {{JWT}}" The format of your header content The format is affected by localStorageName headerFormat String "Authorization" The format of your header authorization key apiSpecs Object null Routes where you want to use the JWT_Token responseSchemas Object {http: {success : 'data', error : 'data'}, socket : {success : null, error : null}} schemas of your API's responses. Where the data can be found in your response object

The format of headerContent is affected by localStorageName if you change the default value

{ localStorageName : ' myJWT ' , headerContent : ' MyContent {{myJWT}} ' }

For apiSpecs you just have to set the unpluralized modelName or primarymodelAssociatedmodels with an auth property inside which contains an array of actions to authenticate. Just follow conventions given above.

{ host : ' http://your-api-host ' , prefix : ' my-prefix ' , pluralizeModels : false , socket : true , localStorageName : ' MyJWT ' , headerContent : ' AuthBearer {{MyJWT}} ' , headerFormat : ' AuthBearerHeaderKey ' apiSpecs : { coach : { auth : [ ' findCoaches ' , ' createCoach ' , ' updateCoach ' ] } , coachComments : { auth : [ ' addCommentToCoach ' , ' removeCommentFromCoach ' ] } } }

Results

findPerson -> will hit GET http://your-api-host/my-prefix/people/:id findPeople -> will hit GET http://your-api-host/my-prefix/people

With pluralizeModels : false findPerson -> will hit GET http://your-api-host/my-prefix/person/:id findPeople -> will hit GET http://your-api-host/my-prefix/person

Actions

Names

There is a maximum of 11 actions for a given model which will be automatically understood by reducers. 3 status actions are dispatched for every redux-crud-async action : START, SUCCESS and ERROR. They are automatically understood by reducers. eg. primary model = channel , associated model = tag

3 primary

actionName url param state state Type findChannel GET channels/:id String channel Object findChannels GET channels?request String channels [Object] createChannel POST channels Object OR FormData channel Object updateChannel PUT channels Object channel Object destroyChannel DELETE channels Object channel Object

You can submit a FormData to create but the FormData is transmitted as is and model will not be appended to the state. If you need the model to be appended to the state, use a javascript object instead of a FormData. FormData can be used to send specific CRUD actions like uploading an image

3 association

actionName url parameters state state Type Comment findChannelTags GET channels/:channelId/tags/:tagId? String, String channelTags [Object] addTagToChannel POST channels/:channelId/tags/:tagId? String, String OR Object channelTag Object if no tag id is set you must give an object to this function createChannel POST channels String, String channelTag Object

Additionnal actions

An additionnal action exists which empties reducers. Dispatch manually this action to empty your reducers.

{ type : ' EMPTY_CHANNEL ' } { type : ' EMPTY_CHANNELS ' } { type : ' EMPTY_PRIMARY_ASSOCIATED_MODELS ' }

Have a look in primaryActionGenerator and associationActionGenerator and Actions Details for more precision about dispatched actions.

States (Reducers)

Reducers return the following states usable in your components

state type channel Object isFindingChannel Boolean channels [Object] isFindingChannels Boolean channelTags [Object] isFindingChannelTags Boolean

See reducers :

TODO

make API endpoints editables

add cache for get requests

cache timeout by route

remove arrow functions in tests

comment code

find a way to test FormData in createModel

add single actions (signup, signin)

add coverage

move associated record uuid generation from associationActions dispatch to reducer

state immutability

add tests for caching

add some headers tests

Change Log

removed second parameter from update action

update and delete primary actions

fix bug when building with webpack - io was undefined

fix silly bugs and add tests for them

API expectation editables