A module for removing the repetitive work in configuring asynchronous requests in xstream-store.
xstream-store-resource
has xstream
and xstream-store
as peer dependencies.
$ npm i xstream xstream-store xstream-store-resource
import createResource from 'xstream-store-resource';
const config = {
// create, find, get, patch, remove, update
effects: ['create', 'get'],
name: 'users',
url: '/api/users',
};
const {actions, actionTypes, streamCreator, effectCreators} = createResource(config);
const actions = {...xs, ...ats};
export {actions, effectCreators, streamCreator};
// store.js
import createStore from 'xstream-store';
import {user$Creator, userEffectCreators} from './streams/user';
const streamCreators = {
users: user$Creator,
};
const effectCreators = [
...userEffectCreators,
];
const store = createStore(streamCreators, effectCreators);
export default store;
import store from './store';
import {actions as userActions} from './user-resource';
const subs = store.state$.subscribe({
next({users}) {
console.log(users)
},
});
/* console.log: initial user state
{
response: null,
requestState: 'idle',
requestEffect: 'idle',
lastError: {},
}
*/
// get user with id '1'
store.dispatch(userActions.get('1')
/* console.log: user request made, response pending
{
response: null,
requestState: 'REQUESTING',
requestEffect: 'GETTING',
lastError: {},
}
*/
/* console.log: user response received
{
response: {id: 1, name: 'Joe Soap', ...},
requestState: 'SUCCESS',
requestEffect: 'IDLE',
lastError: {},
}
*/
// create a user
store.dispatch(userActions.create({username: 'someone@me.com', name: 'Jane Doe'})
/* console.log: user request made, response pending
{
response: {...currUserDetails},
requestState: 'REQUESTING',
requestEffect: 'CREATING',
lastError: {},
}
*/
/* console.log: user response received
{
response: {id: 2, name: 'Jane Doe', ...},
requestState: 'SUCCESS',
requestEffect: 'IDLE',
lastError: {},
}
*/
createResource
takes a single config object, and returns generated actions, action types, and a stream and effects creator that must be passed to an xstream-store
.
import createResource from 'xstream-store-resource';
// defaults
const config = {
// required configs
name: 'my-resource-name',
url: '/my-resource-endpoint/:with/:params',
// optional configs
// base URL to append url config to, e.g. http://my-domain.com
baseUrl: '',
// custom effect creators you want subscribed to this state stream
customEffectCreators: [],
// types of requests to create subscriptions for
effects: [
'create', // POST a new entity
'find', // GET a list of resource entities
'get', // GET a single entity
'patch', // PATCH an entity
'remove', // DELETE an entity
'update' // PUT an entity
],
// How requests will be made, i.e. fetch, jQuery, axios, etc.
// Uses a fetch provider by default
provider: (requestUrl, data, config) => {...},
// configure requests based on the effect, e.g. custom headers for POST
requestConfig: (effect) => {},
};
// createResource returns the following object
const {
// an object containing actions to dispatch for this resource
actions,
// The generated actionTypes used by the above actions
// Useful if you want to create your own action creators to dispatch,
// or if you are creating your own effect creators
actionTypes,
// the stream creator that must be passed to xstream-store's createStore
streamCreator,
// an array of effect creators that must be passed to xstream-store's createStore
effectCreators,
} = createResource(config);
Similarly to Redux, xstream-store
relies on dispatched actions to update the state stream. xstream-store-resource
generates all the actions and action types necessary to make HTTP requests:
import {actions, actionTypes} from './user-resource'
// the following action types can be used to filter other streams in the store
/*
actionTypes = {
CREATE: '@users/create',
CREATE_SUCCESS: '@user/createSuccess',
CREATE_FAILURE: '@user/createFailure',
FIND: '@users/find',
FIND_SUCCESS: '@users/findSuccess',
FIND_FAILURE: '@users/findFailure',
GET: '@users/get',
GET_SUCCESS: '@users/getSuccess',
GET_FAILURE: '@users/getFailure',
PATCH: '@users/patch',
PATCH_SUCCESS: '@users/patchSuccess',
PATCH_FAILURE: '@users/patchFailure',
REMOVE: '@users/remove',
REMOVE_SUCCESS: '@users/removeSuccess',
REMOVE_FAILURE: '@users/removeFailure',
UPDATE: '@users/update',
UPDATE_SUCCESS: '@users/updateSuccess',
UPDATE_FAILURE: '@users/updateFailure',
}
*/
// action creators that can be dispatched for your resource using store.dispatch
/*
const actions = {
reset: () => {...},
create: (data, params, extra) => {...},
find: (params, extra) => {...},
get: (id, params, extra) => {...},
patch: (id, data, params, extra) => {...},
update: (id, data, params, extra) => {...},
remove: (id, data, params, extra) => {...},
});
*/
The above actions are the actions that are important as an end user, but a full list can be found in src/action-creators.ts.
Parameter | Description |
---|---|
id | appended to url e.g. for the endpoint /users userActions.get(1) will make a request for /users/1 |
data | data sent as the payload of the request |
params | url parameters to replace. e.g. for the endpoint /articles/:articleId/comments articleComments.get(1, {articleId: 2}) will make a request for /articles/2/comments/1 |
extra | additional data to send with the request. Currently supports adding query parameters via the query property e.g. for the endpoint /users userActions.get(1, {}, {query: {name: true; age: true}) will make a request for /users/1?name=true&age=true |
Effects map to HTTP functions to make it easy to define what to do with a resource. The config allows only specific effects to be specified for a resource. Each effect requires its own subscription, it's best to specify in the config which effects to create subscriptions for.
Each effect's subscription updates the resource's state stream when a related action for that effect is dispatched.
When the create
, find
, get
, patch
, remove
, and update
action creators are dispatched, the resource will immediately be in a state indicating that a response is pending:
// dispatch patch on a user
{
response: {...userDetails},
requestState: 'REQUESTING',
requestEffect: 'PATCHING',
lastError: {},
}
Once the request is resolved or rejected, the resource's state will be updated:
// success
{
response: {...userDetails},
requestState: 'SUCCESS',
requestEffect: 'IDLE',
lastError: {},
}
// failure
{
response: {...userDetails},
requestState: 'FAILURE',
requestEffect: 'IDLE',
lastError: {message: 'request failed'},
}
- document API
- examples