Record was saved to the server, but the response returned the new id 'null'. The store cannot assign a new id to a record that already has an id

#1

Hi, I’m currently building a portfolio app that talks to a mongoDB API. I wrote the API and it’s fairly simple, this is the POST route

router.put('/:id', (req, res, next) => {
    console.log(req.body);
    Study.findByIdAndUpdate(req.params.id, req.body, {
        new: true,
        upsert: true
    }, (err) => {
        if (err) {
            err.status = 400;
            return next(err)
        }
        res.json({ message: 'Updated!' });
    });
});

I believe I’m setting up the proper Adapter and Serializer:

import DS from 'ember-data';
export default DS.RESTAdapter.extend(DS.BuildURLMixin, {
    namespace: 'api/v1',
    host: 'http://localhost:5000',
    updateRecord(store, type, snapshot) {
        let data = {
       
        };
    
    let serializer = store.serializerFor(type.modelName);
    serializer.serializeIntoHash(data, type, snapshot);
    debugger;

    let { id } = snapshot;
    let url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');
    console.log(data.title);

 

    return this.ajax(url, 'PUT', { data: data });
  }

   import DS from 'ember-data';

   export default DS.RESTSerializer.extend({
    normalizeResponse(store, primaryModelClass, payload, id, requestType) {
        payload = {
            "studies": payload
        };

        return this._super(store, primaryModelClass, payload, id, requestType);
    },
    primaryKey: '_id'
    
});

and my controller and template seem to be right:

<h2>Edit</h2>
<form onSubmit={{action "updateTitle"}}>
    {{input value=model.title}}
    <button type="submit">Update</button>
</form>

import Controller from '@ember/controller';

export default Controller.extend({
    actions: {
        updateTitle(e) {
            e.preventDefault();
            let title = this.get('model');
            title.save()
            this.transitionToRoute('case-studies');
        }
    }
});

The record saves, but I get this error in my browser console:
Assertion Failed: 'study:5cdb30ca2fcde3c9ba7974ce' was saved to the server, but the response returned the new id 'null'. The store cannot assign a new id to a record that already has an id.

I’m still somewhat new to ember, I can’t see what I’m doing wrong.

#2

What does the response from your backend look like? Seems like either it’s not returning the id or Ember is having trouble finding the id when normalizing the payload. The error means that you have a record with an id and the normalized response would be clobbering that id.

#3

My response from the backend looks like this:

For findAll:

[
{
    "_id": "5cdb30ca2fcde3c9ba7974ce",
    "title": "Data",
    "__v": 0
},
{
    "_id": "5cdb3109aeb661cab4cf344c",
    "title": "Purple",
    "__v": 0
},
{
    "_id": "5cdb31fa85b86acd9876137f",
    "title": "New id",
    "__v": 0
}
]

And a single response:
{
"_id": "5cdb30ca2fcde3c9ba7974ce",
"title": "Data",
"__v": 0
}
#4

Sorry I meant the response for the PUT. Actually now that I look at your API code you posted I think the problem is more obvious. You’re returning a response body that looks like this:

{ message: 'Updated!' }

But Ember Data expects the full (updated) record in the response for a PUT, so it’s trying to smash that JSON into the store and overwrite the record contents with it, and since that JSON has no id it’s saying “hey you can’t overwrite this record with a null id”. Ideally you should just return the updated record in the PUT handler, but if you really don’t want to do that you could handle the case in your adapter/serializer, something along the lines of “update the record state but not the record content”.

#5

I’m sorry, I don’t understand what you mean. Would you mind showing me an example? Thanks for your help.

#6

This is the response from the PUT I believe:

   {
    study: { 
    title: 'First Case Study'
    } 
   }
#7

ok so I think that’s your problem there… there’s no “id” field in the PUT response. Ember Data by default expects a “full formed” record from a PUT, it uses the server’s response as the source of truth about the state of the record. So your backend should be returning essentially the exact same thing it does for the GET detail/single. Does that make sense? Essentially Ember Data (by default) expects an updateRecord operation (which yields a PUT request) to have a full record with id and everything. Basically a PUT+GET.

#8

Ah, I have it now I think, the following updated successfully with no console errors

   updateRecord(store, type, snapshot) {
    let data = {};
    let serializer = store.serializerFor(type.modelName);

    serializer.serializeIntoHash(data, type, snapshot);

    let id = snapshot.id;
    let url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');
    data = {
      _id: id,
      title : data.study.title
    }
    return this.ajax(url, 'PUT', { data: data });
}

Thanks for your help, this was killing me.

#9

Awesome! Glad you got it working. Ember Data is awesome once you get everything set up right but sometimes it’s a lot of fiddling to get there.