Apollo-server: RESTDataSource - cannot convert object to primitive

Created on 15 Aug 2018  ·  26Comments  ·  Source: apollographql/apollo-server

Hi,
I have question about RESTDataSource docs:
https://www.apollographql.com/docs/apollo-server/features/data-sources.html#HTTP-Methods

What have you passed as a movie in post/put/patch requests? Because it seems that you can't pass object there. You should JSON.strigify it first. Otherwise you will get "cannot convert object to primitive" from node-fetch. Am I right?

Here is my data source:

export default class DataSource extends BaseDataSource {
    uri = '/travelSeasons';

    async getTravelSeasons() {
        return this.get(this.uri);
    }

    async addTravelSeason(travelSeason: TravelSeason) {
        return this.post<TravelSeason>(this.uri, travelSeason);
    }

    async updateTravelSeason(travelSeason: TravelSeason) {
        return this.patch<TravelSeason>(this.uri + `/${travelSeason.id}`, travelSeason);
    }
}

And when I comment lines below:

export default class BaseDataSource extends RESTDataSource {
    baseURL = config.API_URL;

    // protected post<TResult = any>(path: string, body?: Body, init?): Promise<TResult> {
    //     return super.post(path, JSON.stringify(body), {
    //         headers: {
    //             "Content-Type": "application/json"
    //         },
    //         ...init
    //     });
    // }

    ...
}

I will get this:

{
  "data": {
    "addTravelSeason": null
  },
  "errors": [
    {
      "message": "Cannot convert object to primitive value",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "addTravelSeason"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot convert object to primitive value",
            "    at String (<anonymous>)",
            "    at Request.Body (/usr/src/app/node_modules/node-fetch/lib/index.js:172:10)",
            "    at new Request (/usr/src/app/node_modules/node-fetch/lib/index.js:1169:8)",
            "    at DataSource.<anonymous> (/usr/src/app/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:140:29)",
            "    at Generator.next (<anonymous>)",
            "    at fulfilled (/usr/src/app/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:4:58)",
            "    at <anonymous>",
            "    at process._tickCallback (internal/process/next_tick.js:188:7)"
          ]
        }
      }
    }
  ]
}

My node version is: 8.11.3, all libraries are up to date.

Btw what about "Content-Type": "application/json" header? Is there a good way to add it to my datasource by default (in base class or sth - I guess some kind of adapter) ? Is my solution ok?

👩🏾‍🎨 needs-design

Most helpful comment

As a temp solution used this:

this.post('records', new Object({ ...data }));

All 26 comments

Were you able to find a fix for this?

I have the same problem, the object is not parsed here part of my code:

//resolver.js
const resolver = {
  Mutation: {
    async addInvite(_root, {invite}, { dataSources }) {
      return await  dataSources.invitesAPI.addInvite(invite);
    }
  }
};
//datasource.js
const INVITES_PATH = 'invites'

class InviteDataSource extends RestDataSource {
  constructor() {
    super();
  }  
  async addInvite(invite) {
    return await this.post(invite)
  }
}

This is not the real code, but the logic is the same. For some reason invite is not recognized as an object, it doesn't pass the following test in RESTDataSource.ts

//RESTDataSource.ts
   // We accept arbitrary objects as body and serialize them as JSON
   if (
      options.body !== undefined &&
      options.body !== null &&
      (options.body.constructor === Object ||
        ((options.body as any).toJSON &&
          typeof (options.body as any).toJSON === 'function'))
    ) {
      options.body = JSON.stringify(options.body);
      options.headers.set('Content-Type', 'application/json');
    }

invite is a valid object, maybe created with Object.create, but I think it should be stringified.
To solve this I have to recreate object doing something like this:

//resolver.js
const resolver = {
  Mutation: {
    async addInvite(_root, {invite}, { dataSources }) {
      return await  dataSources.invitesAPI.addInvite({...invite});
    }
  }
};

Hi guys,

Trying to play with mutations and ?m facing the same problem.

Sounds like the check in the RestDataSource is not absolutely correct. In fact, by checking that option.body.toJSON is a method, means that you expect that the object has a "custom toJSON" behavior.

What is strange is that you don't call it in the if body :)

I would personally expect something like

if (options.body !== undefined && options.body !== null && typeof(options.body) === 'object') {

    options.headers.set('Content-Type', 'application/json');

    options.body = ((options.body as any).toJSON 
                    && typeof (options.body as any).toJSON === 'function')
        ? options.body.toJSON()
        : JSON.stringify(options.body);

} 

do you mind if I create a PR with the suggested change ?

As a temp solution used this:

this.post('records', new Object({ ...data }));

+1 for @fantactuka's solution here: spreading the object into a new object works.

Thanks very much for reporting this! We think this is a great idea, and would definitely review a docs PR. If you (or anyone else) is interested in working on this, that would be great - thanks!

Will there be any real fix for this? Because spreading the object is just a quickfix I presume.

Yeah that's just a stopgap solution to make up for being unable to trace a tangle through spaghetti code.

I just came across this workaround mentioned by @fantactuka , which shouldn't be used anymore, specially if this was posted 2 years ago as an issue and it hasn't been solved yet. Is there going to be a fix for this soon?

Yeah, we faced the same problem in our team. How can we help to fix it ?

Following as I am facing the same issue in a project I recently embarked on.

I think this issue should be reopened as it's not fixed.

I am having same issue

This is what I did to avoid running into this issue entirely, though it would be nice if the library could properly handle it itself:

class BaseApi extends RESTDataSource {
  willSendRequest(request){
        if (request.body && typeof request.body === 'object') {
      request.body = { ...request.body };
    }
  }
}

two years in, can we please at least get this issue reopened?

+1, Please reopen the issue

Does anyone have a proposal for the change they'd like to see here? @jfreyre's https://github.com/apollographql/apollo-server/issues/1539#issuecomment-471874823 is the closest to an actual suggestion. A PR is certainly welcome, so long as it's backwards-compatible and comes with tests!

I am also having the same issue.

I reiterate @abernix 's note — happy to review a backwards-compatible tested PR.

B7666761-7710-4F66-A65F-688945D11FA9

But yeah don't use any of that to fix the glaring issues you leave developers to deal with.

B7666761-7710-4F66-A65F-688945D11FA9

But yeah don't use any of that to fix the glaring issues you leave developers to deal with.

The only time you can demand fixes is when you pay for a product.

This is still open-source, don't forget it.

As much as I'd like the team to fix many issues that affects my work too, and despite having been completely ignored by them in the issues I opened, I am not mad at them because this is still OSS.

Not trying to come off demanding fixes. Trying to point out how ridiculous it is when a company raises 22 million US dollars on a platform to make developers lives easier and expects other developers to fix their issues for free. Apollo should use some of that 22 million to fix it, either internally, or with a bounty program. Not to close issues that affect people only to reopen them years later saying "fix it yourself pls".

Also, my team does pay for Apollo. Not this library specifically, no, we pay for noisy Slack notifications.

This level of support would be acceptable from a project ran by someone in their spare time. Not one with a large VC backing that also gets funding from subscriptions.

@mikew You'll be happy to know that after 9 months of Apollo Server having no full-time staff, this project has returned to having active maintenance, and it's certainly my intention to not allow it to be left unstaffed again.

This does seem like an issue worth fixing. There are no full reproductions given above, though. For example, in @topekTopperson 's original post, there's no information on what the TravelSeason type is or how you create one.

I'll be happy to reopen and evaluate this issue if somebody can provide a full reproduction (eg, a git repo or codesandbox.io link with full instructions on how to see the issue for myself) of it!

Hello people,
sorry for my absence in this topic but I am not using Apollo Server anymore so that is why I didn't provide more examples / PR or whatever.
I have been tracking this issue though and I think that @mikew has a very good point here :)

I just hope that this lack of reproduction is not an excuse now.
When you asked developers here for that PR, missing codesandbox was not an issue, suddenly it is.
I had even that impression that this problem was understood by others.
Anyway I am looking forward for that fix just because it was me who started this :smile:

It's possible that my colleagues who responded previously understood the particular bug on a deeper level than I did.

I am a very simple person who loves the stages of "there's something broken that I can see on my computer, let's go fix it" and does not love the stages of "somebody told me that something is wrong but I have to do a lot of work to see it broken for myself". I'm newly back to focusing on open source for the first time in years, and I hope that folks will be happy with my ability to improve the project when given clear examples of what the problem is. The tradeoff is I'm going to make a lot of comments of the form "sorry, I don't understand how to reproduce this, please give me a reproduction", but I promise that if you do give me that reproduction I'm pretty likely to get you what you want relatively quickly :)

I opened a PR to fix this in #5070.

Was this page helpful?
0 / 5 - 0 ratings