I need to make a POST to a server-side API. I must send an id
key into the request body to the server.
I use a Backbone model. But when I do:
myModel.set("id", somevalue)
myModel.save()
The network request that is fired is : URL/someValue [PUT]
Backbones doesn't do a POST but a PUT and appends the id to the url.
So I just want to pass an id
key to the server without Backbone noticing.
From Backbone's doc:
Backbone is pre-configured to sync with a RESTful API.
[...]
The default
sync
handler maps CRUD to REST like so:
- create →
POST /collection
- read →
GET /collection[/id]
- update →
PUT /collection/id
- patch →
PATCH /collection/id
- delete →
DELETE /collection/id
A new entry doesn't have an ID, so if you give an ID to the model before saving it, Backbone defaults to a PUT request because it thinks you want to save an existing entry.
Choose one of the following solutions.
This one is the obvious one. If you can, stick to the standard.
Change the API to handle PUT/PATCH requests and only use POST on creation. Make the API endpoint take the ID from the URL.
type
option1Simple and works really well for a one-off situation.
Every options passed to save
(or fetch
) overrides the options the sync
function defines by default and passes to jQuery.ajax
function.
// Make the request, allowing the user to override any Ajax options. var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
var url = model.url(); // get the url before setting the `id`
model.save({
id: somevalue
}, {
url: url, // fix the url
type: 'POST' // choose the HTTP verb
});
Fixing the url that the model uses is simple, you have also some choices:
url
option (like above)url
function of the modelOverriding the url
function (source) works well for situation where every call should use a specific url, without the default id
appended to it.
var MyModel = Backbone.Model.extend({
url: function() {
return _.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
}
});
idAttribute
on the modelThis depends on what the id
you're trying to pass means in the data.
Backbone Model uses "id"
has the default id attribute name. You can specify a different name by overriding the idAttribute
property of the model. Whatever the name, it is always automatically made available through the model.id
property.
Now, assuming the id
attribute isn't related to this model and this model's real id attribute name is something like UID
, you could change the idAttribute
of the model to reflect the real name of the attribute (or it could even be a string that's never going to be an attribute).
var MyModel = Backbone.Model.extend({
idAttribute: 'UID',
});
Now, the id
attribute is not considered an id for the current model, and model.isNew()
will return true
, sending a POST request to create it on save.
sync
/save
function behaviorIf the API you're using is not RESTful, you can adjust the behaviors by overriding the sync
function. This can be done on the model or collection, or on the Backbone.sync
function which is used by default by the collections and models.
For example, if you wanted to make every request use POST by default for MyModel
class:
var MyModel = Backbone.Model.extend({
sync: function(method, model, options) {
return Backbone.sync.call(this, method, model,
_.extend({ type: 'POST' }, options));
}
});
You could do something similar with only the save
function to let the fetch
do its GET
request as usual.
emulateHTTP
setting2If you want to work with a legacy web server that doesn't support Backbone's default REST/HTTP approach, you may choose to turn on
Backbone.emulateHTTP
. Setting this option will fakePUT
,PATCH
andDELETE
requests with a HTTPPOST
, setting theX-HTTP-Method-Override
header with the true method.[...]
Backbone.emulateHTTP = true; model.save(); // POST to "/collection/id", with "_method=PUT" + header.
isNew
Has this model been saved to the server yet? If the model does not yet have an id, it is considered to be new.
Some other answers on this site suggest overriding the isNew
function. Don't. The function has its purpose and overriding it to force a POST request is a poor hack, not a solution.
isNew
is used internally but can also be used by your code or other libraries and Backbone plugins.
1 While I did not take this from stack overflow, it was already an answer by Andrés Torres Marroquín on a similar question.
2 Taken from Maanas Royy's answer.