Apollo-server: ID cannot represent value: {_bsontype: \"ObjectID\" I get this error when I want to get a response

Created on 8 Sep 2018  路  23Comments  路  Source: apollographql/apollo-server

Most helpful comment

@phao5814, thank you so much for the hint :smile:

I put it just before the apollo server and it worked finally.

const { ObjectId } = mongoose.Types;
ObjectId.prototype.valueOf = function () {
  return this.toString();
};

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => ({
    SECRET: config.secret,
  }),
  formatError(error) {
    return {
      message: error.message,
      code: error.originalError && error.originalError.code,
      locations: error.locations,
      path: error.path,
    };
  },
});

apolloServer.applyMiddleware({ app, path: '/graphql' });
app.use('/graphql', () => {});

All 23 comments

Even I am getting same error

I found a solution, you must convert _id to String by using .toString() before return it

@lagmanzaza at many places I am returning entire object returned by mongoose. How to do it in generic way?

I'm not sure. I alter those object then I convert _id from ObjectId() to string by using .toString() instead. It does work.

GraphQL have used a new method to serialize ID, the new method doesn't support ID in object type anymore, but following codes will help on this:

ObjectId.prototype.valueOf = function () {
    return this.toString();
};

You can also create a scalar type, as shown here: https://github.com/apollographql/apollo-server/issues/1649#issuecomment-420840287

@Lanfei how can i use what you proposed to get ID working again on a 3rd party package?

ObjectId.prototype.valueOf = function () {
    return this.toString();
};

@williamli Which package? mongoose?

@williamli
Try:

// If you are using mongoose:
const ObjectId = require('mongoose').Types.ObjectId;
// If you are using mongodb:
const ObjectId = require('mongodb').ObjectId;
// Otherwise:
const ObjectId = require('bson').ObjectId;

ObjectId.prototype.valueOf = function () {
    return this.toString();
};

@Lanfei I tried this by including the following and the result is that I get [object Object] in the id field.

const { ObjectId } = mongoose.Types;
ObjectId.prototype.valueOf = () => {
    return this.toString();
};

image

@phao5814 that's because inside of a lambda function (one denoted by () =>) the this reference is copied from the outer closure scope. If you define it in the root scope, this ends up being the same as the global scope, i.e. process in node or window in the browser.

If you want this to work you need to say:

const { ObjectId } = mongoose.Types;
ObjectId.prototype.valueOf = function () {
  return this.toString()
}

Again, you can always define a custom scalar type, as per https://github.com/apollographql/apollo-server/issues/1649#issuecomment-420840287

I get the error on a simple ...all query, as soon as I ask for _id . I did read what was here, but really not sure WHERE to fix this. I m on a minimal mongoose apollo-server-express react (cra) setup . I'm new at apollo server.. rough start .

export const GET_ALL_BLOGS = gql`
  query {
    getAllBlogs {
    if I put _id here (client) I get the error. 
      title
      author
      body
      date
    }
  }
`;

Just create a virtual attribute ID convert it to string to be parse by the graphql server

Put this below in your schema file and just query for 'id'

TheSchema.virtual('id').get(function () {
  return this._id.toString();
});

@davae1an great solution!

@Lanfei

I tried your example but still getting same error. Where do you put this code snippet? :sweat_smile: Thanks.

// If you are using mongoose:
const ObjectId = require('mongoose').Types.ObjectId;
ObjectId.prototype.valueOf = function () {
return this.toString();
};

@arvi I placed that snippet of code above my schema definitions.

// ... Some more imports here...
const {
    GraphQLObjectType,
    GraphQLID,
    GraphQLString,
    GraphQLSchema,
    GraphQLInt,
    GraphQLList,
    GraphQLFloat,
    GraphQLBoolean,
} = graphql;

const { GraphQLDateTime } = graphqlDateTime;

const { ObjectId } = mongoose.Types;
ObjectId.prototype.valueOf = function () {
    return this.toString();
};

const ImageType = new GraphQLObjectType({
    name: "Image",
    fields: () => ({
        ...
    }),
});

// ... Some more definitions here

@phao5814, thank you so much for the hint :smile:

I put it just before the apollo server and it worked finally.

const { ObjectId } = mongoose.Types;
ObjectId.prototype.valueOf = function () {
  return this.toString();
};

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => ({
    SECRET: config.secret,
  }),
  formatError(error) {
    return {
      message: error.message,
      code: error.originalError && error.originalError.code,
      locations: error.locations,
      path: error.path,
    };
  },
});

apolloServer.applyMiddleware({ app, path: '/graphql' });
app.use('/graphql', () => {});

I think that it is better to put the toString() inside of the _spread operator_. This would allow anyone who reads the code to know what is happening.

For an event model in Mongoose:

            return event
                .save()
                .then(r => {
                    console.log('mongo event saved', r._doc);
                    return { ...r._doc, _id: event._doc._id.toString() };
                })
                .catch(err => {
                    console.error(err);
                    throw err;
                });

This is less _generic_ but more readable.

i'm solved the error with toString() i put the toString() inside of the schema definition

addMarket: (root, { marketName, marketLogo }) => {
            return new Promise((resolve, reject) => {
                const newMarket = new Market({
                    id: (new mongoose.Types.ObjectId()).toString(),
                    marketName,
                    marketLogo,
                });
                newMarket.save((err, res) => {
                    err ? reject(err) : resolve(res);
                });
            });
        }

I'm going to ask the same question than on #1649:

Has this problem been fixed in the meantime?

We used to do stuff like id: obj => obj._id.toHexString() in all our resolvers that handled MongoDB ObjectIds, but now it seems id: obj => obj._id alone is working perfectly fine.

I get trapped in this error while populating, and the solutions above can't work for me.


Well, in my situation, I need to set a transformer to ensure Document.prototype.toJSON outputs a string instead of an object.

SampleSchema.set('toJSON', {
  transform(doc, ret) {
    return JSON.stringify(ret);
  },
});

Just create a virtual attribute ID convert it to string to be parse by the graphql server

Put this below in your schema file and just query for 'id'

TheSchema.virtual('id').get(function () {
  return this._id.toString();
});

Where exactly in the schema file? please let me know :)

Was this page helpful?
0 / 5 - 0 ratings