Skip to content

Instantly share code, notes, and snippets.

@akre54
Created July 8, 2015 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akre54/8fd79bf8d778814d506b to your computer and use it in GitHub Desktop.
Save akre54/8fd79bf8d778814d506b to your computer and use it in GitHub Desktop.
Reflux REST mixin

Call actions on stores RESTfully:

// C
BotActions.save(null, {name: 'Curly', age: 26}); // POST a model without an id

// R
BotActions.fetch(); // GET the whole Collection

BotActions.fetch(2); // GET a single model by id

// U
BotActions.save(2, {name: 'Curly', age: 27}); // PUT a model by id

// D
BotActions.delete(2); // DELETE a model by id

All actions return promises:

BotActions.delete(2).then((id) => alert(Deleted ${id}))

import Reflux from 'reflux';
import assign from 'object-assign';
import RESTActions from './rest_actions';
const BotActions = Reflux.createActions(assign({}, RESTActions, {
}));
export default BotActions;
import Reflux from 'reflux';
import assign from 'object-assign';
import RESTMixin from './rest_mixin';
import BotActions from './bot_actions';
const urlRoot = 'bots';
let _bots = [];
const _byId = {};
const BotStore = Reflux.createStore({
listenables: BotActions,
mixins: [RESTMixin(urlRoot, BotActions)],
onFetchCompleted(resp) {
// Single model
if (resp.id) {
// Exists
let existing;
if (existing = _byId[resp.id]) {
assign(existing, resp);
// Add it
} else {
_byId[resp.id] = resp;
_bots.push(resp);
}
// Whole collection
} else {
_bots = resp;
}
this.trigger();
},
onFetchfailed(err) {
// do something
},
onSaveCompleted(resp) {
assign(_byId(resp.id), resp);
@trigger();
},
onSaveFailed(err) {
},
onDeleteCompleted(id) {
},
onDeleteFailed(id, err) {
}
getBots() {
return _bots;
}
});
export default BotStore;
export default {
'fetch': { asyncResult: true },
'save': { asyncResult: true },
'delete': { asyncResult: true }
};
// Mixin RESTful sync behaviors to Reflux stores. Calls the .completed or .failed
// action when the promise is fufilled.
import sync from './sync';
export default (urlRoot, Actions) => {
const getUrl = (id) => {
let base = typeof urlRoot === 'function' ? urlRoot() : urlRoot;
return id ? `${base}/${id}` : base;
}
return {
// Returns a single model by id, or all models if no id passed.
fetch(id, params) {
let method = 'GET';
let url = getUrl(id);
sync(url, {method, body: params})
.then(Actions.fetch.completed)
.catch(Actions.fetch.failed);
},
// Persists a single model to the server. Creates the model if the id doesn't
// exist, or updates the record if it does.
save(id, data) {
let method = id ? 'PUT' : 'POST';
let url = getUrl(id);
sync(url, {method, body: data})
.then(Actions.save.completed)
.catch(Actions.save.failed);
},
// Deletes a single model by id (required).
delete(id) {
if (!id) throw new Error('need an id to delete');
let method = 'DELETE'
let url = getUrl(id);
sync(url, {method})
.then(Actions.delete.completed.bind(this, id))
.catch(Actions.delete.failed.bind(this, id));
}
};
};
// A simple wrapper around the window.fetch API. Returns a Promise and sets the
// proper config variables for the request.
import 'whatwg-fetch';
import defaults from 'defaults';
import AppStore from 'stores/app';
const stringifyGETParams = (url, data) => {
let query = '';
for (let key in data) {
if (data[key] == null) continue;
query += '&'
+ encodeURIComponent(key) + '='
+ encodeURIComponent(data[key]);
}
if (query) url += (~url.indexOf('?') ? '&' : '?') + query.substring(1);
return url;
};
const checkStatus = (response) => {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
const error = new Error(response.statusText);
error.response = response;
throw error;
}
};
export default (url, options = {}) => {
const config = AppStore.getConfig();
if (options.method === 'GET' && typeof options.body === 'object') {
url = stringifyGETParams(url, options.body);
delete options.body;
}
options.headers = defaults(options.headers || {}, {
'access-token': config.accessToken,
'Accept': 'application/json',
'Content-Type': 'application/json'
});
if (typeof options.body === 'object') {
options.body = JSON.stringify(options.body);
}
return window.fetch(`${config.apiHost}/${url}`, options)
.then(checkStatus)
.then(response => response.json() );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment