Skip to content

Instantly share code, notes, and snippets.

@noahgrant
Forked from akre54/backbone.fetch.js
Last active November 8, 2018 16:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noahgrant/d3ea7eb7e65e865a41165447c0a2e0b6 to your computer and use it in GitHub Desktop.
Save noahgrant/d3ea7eb7e65e865a41165447c0a2e0b6 to your computer and use it in GitHub Desktop.
Backbone.ajax with window.fetch
import _ from 'underscore';
import Backbone from 'backbone';
import {stringify} from 'qs';
const backboneSync = Backbone.sync.bind(Backbone);
const MIME_TYPE_JSON = 'application/json';
const MIME_TYPE_DEFAULT = 'application/x-www-form-urlencoded; charset=UTF-8';
Backbone.sync = (method, model, options) => {
// Backbone creates an `xhr` property on the options object for its default
// xhr request made via jquery. Using window.fetch this becomes just a reference
// to a Promise, and not very useful. So here we attach a response object that we
// mutate directly with the request's response object. Note that we the
// original options object passed to fetch/save/destroy calls (and kept in
// closure) is not the same one passed to Backbone.ajax. It's a copy, and so
// we must modify the response object directly for it to be passed through.
options.response = {};
return backboneSync(method, model, options);
};
Backbone.ajax = function(options={}) {
var hasData = !!_.size(options.data),
hasBodyContent = !/^(?:GET|HEAD)$/.test(options.type) && hasData;
if (options.type === 'GET' && hasData) {
options.url += (options.url.indexOf('?') > -1 ? '&' : '?') + stringify(options.data);
}
return window.fetch(options.url, {
...options,
method: options.type,
headers: {
Accept: MIME_TYPE_JSON,
// these defaults are the same as jquery's:
// * only set contentType header if a write request and if there is body data
// * default to x-www-form-urlencoded. Backbone will pass 'application/json'
// (and JSON-stringify options.data) for save/destroy calls
...(hasBodyContent ? {'Content-Type': options.contentType || MIME_TYPE_DEFAULT} : {}),
...options.headers
},
...(hasBodyContent ? {
// Backbone already JSON-stringifies data for save/destroy calls, but for any other
// contentful request, for example a .fetch with {type: 'POST}, we take care of it here
body: typeof options.data === 'string' ?
options.data :
options.contentType === MIME_TYPE_JSON ?
JSON.stringify(options.data) :
stringify(options.data)
} : {})
}).then((res) => {
// make a copy of the response object and place it into the options
// `response` property we created before Backbone.sync. This will make it
// available in our success callbacks. Note that our error callbacks will
// have it, as well, but they will also get it directly from the rejected
// promise. we use _.extend instead of Object.assign because none of the
// Response properties are enumerable
_.extend(options.response, res);
// catch block here handles the case where the response isn't valid json,
// like for example a 204 no content
return res.json().catch(() => ({}))
.then((json) => res.ok ? json : Promise.reject(_.extend({}, res, {json})));
}).then(options.success, options.error).then(options.complete || _.noop);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment