1.1. Install
1.3. Create a resource with a config
2.1. Resource options
2.2. Method options
2.3. Add a custom method
npm i
If your API endpoint looks like a standard REST API, as below
List = GET: /users
Create = POST: /users
Read = GET: /users/1
Update = PUT: /users/1
Delete = DELETE: /users/1
you can use a simple configuration
import { makeResource } from 'crud';
const apiUrl = '/api/users';
const resource = makeResource('users', apiUrl);
If you need more than just a CRUD, or your endpoint differs from standard REST APIs, you can use a custom configuration as below. You can read about configuration params in the Resource Configuration section.
import { makeResource } from 'crud';
const apiUrl = '/api/users';
const resource = makeResource('users', {
baseUrl: apiUrl,
list: {
modifyAfter: modifyListAfter,
hookAfter: usersReceived,
},
create: {
url: `${apiUrl}/create`,
usePayload: true,
modifyBefore: modifyItemBefore,
hookAfter: hookAfterCreate,
},
update: {
url: `${apiUrl}/:id/update`,
modifyBefore: modifyItemBefore,
hookAfter: hookAfterUpdate,
},
delete: {
url: `${apiUrl}/:id/delete`,
usePayload: true,
hookAfter: removeRecord,
}
});
You may use predefined methods or define your own custom method. Each method is an action creator in fact, so you need to dispatch it with bind or directly.
Receive a list of records
Receive a specific record
Create record
Update record
Delete a specific record
An object with CRUD actions
//actions/users.js
import { makeResource } from 'crud';
const apiUrl = '/api/users';
const resource = makeResource('users', apiUrl);
export default resource;
You will have access to your resource data in state.crud.name_of_resource
.
//reducers/index.js
import { combineReducers } from 'redux';
import { crudReducer } from 'crud';
//...
export default combineReducers({
//...
crud: crudReducer,
});
//components/UsersList.jsx
//...
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import users from '../actions/users';
class UsersList extends Component {
componentDidMount() {
this.props.users.list();
}
//...
render() {
return (
<ul>
{this.props.list.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
}
function mapDispatchToProps(dispatch) {
return {
users: bindActionCreators(users, dispatch),
}
}
function mapStateToProps(state) {
return {
list: state.crud.users
};
}
export default connect(mapStateToProps, mapDispatchToProps)(UsersList);
A config should be a URL or an object. If you want your resource to be flexible you need to use an object as a config. A config object should contain at least one option.
An endpoint base URL, uses for generating methods URLs
A method config for receiving of list of records
A method config for receiving a record
A method config for creating a record
A method config for updating a record
A method config for deleting a record
Every predefined method has following options.
An endpoint for a method. You can use placeholders in the definition if you need,
e.g.: '/api/users/:id/update'
. So the placeholder will be replaced with a matched
property from your data, or to ID you passed as payload if the placeholder is :id
HTTP method type (GET
, POST
, PUT
, or DELETE
)
When this property is true, CRUD uses payload passed to a method instead of API response. It can be useful if you want to dispatch data, but server returns some other data structure or you just don't want to rely on a server response.
You can use the method if you want to modify data before sending. For example, you may need to trim service properties.
function beforeUserUpdate(user){
return {
id: user.id,
roles: user.rolesIds
}
}
If you want to change received data format, or get only specific fields you can use this method.
function afterUsersReceive(users){
return users.map(user => ({
...user,
name: `${user.first_name} ${user.last_name}`
}))
}
May be used to dispatch some action before API call
function startUpdate(file){
return {
type: SHOW_SAVE_PROGRESS,
payload: file
}
}
May be used to dispatch some action after API response
function finishUpdate(file){
return dispatch => {
dispatch(hideSaveProgress());
dispatch(closeModal());
};
}
As mentioned above every method is an action creator. So you can make an action creator you need and just pass it as a prop to the resource config. You can even replace a predefined method if you want, you only need to call CRUD actions manually.
//...
const resource = makeResource('documents', {
baseUrl: apiUrl,
list
});
function list() {
return (dispatch, getState) => {
const { documents } = getState().crud;
if(documents) {
return dispatch(hookAfterList(documents));
}
return axios.get(docsUrl)
.then(response => response.data)
.then(data => {
const dataProp = 'assessment_details';
const modified = {
...data,
[dataProp]: data[dataProp].map(row => ({ ...row, id: row.assessment_id }))
};
// call a CRUD action
dispatch(resource.actions.list(modified[dataProp]));
dispatch(hookAfterList(modified));
});
};
}
function hookAfterList(data) {
return (dispatch, getState) => {
// when get from state
if(Array.isArray(data)) {
const { states } = getState().entries;
dispatch(documentsReceived(data, states));
// when receive from API
} else {
const records = data.assessment_details;
const states = data.assessment_definition_states;
dispatch(documentsReceived(records, states));
dispatch(storeEntries('states', states));
dispatch(storeEntries('statesByGroup', groupBy(states, 'assessment_definition_id')));
}
}
}
//...
Whenever you run a method the crud library works as follows:
- Change data by function
modifyBefore()
- Dispatch action
hookBefore()
with modified data - Call to an API
- If the
usePayload
property is true, use payload instead of received data - Change result with
modifyAfter()
- Dispatch an internal CRUD action with modified result
- Dispatch action
hookAfter()
with modified result
[ ] Cover reducers
[ ] Cover batch actions
[ ] Add more examples
Use the npm version
command instead of using git commit
directly to commit module changes.
You should specify version according to SemVer:
Given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
The npm version
command can increase version automatically with one of following values as an argument: patch
, minor
, major
.
In fact there are more possible values, but you need only these three.
- If you get
Git working directory not clean.
message, you can add-f
flag after command