Graphql-js: Unable to query for MongoDB ObjectIDs

Created on 10 Sep 2018  路  23Comments  路  Source: graphql/graphql-js

I recently updated from 0.13.2 to 14.0.2 which includes breaking changes.

This introduced errors with existing queries which include MongoDB Object Ids (probably from #1382):

ID cannot represent value: { _bsontype: \"ObjectID\", id: }

Repository with complete, minimal repeatable example here:

const Thing = mongoose.model('Thing', new mongoose.Schema({
  id: mongoose.Schema.Types.ObjectId,
  name: String
}));

const ThingType = new GraphQLObjectType({
  name: 'thing',
  fields: function () {
    return {
      id: { type: GraphQLID },
      name: { type: GraphQLString }
    }
  }
});

const RootMutation = new GraphQLObjectType({
  name: 'CreateMutation',
  fields: {
    create: {
      type: ThingType,
      description: 'Create new thing',
      args: {
        name: {
          name: 'Name of Thing',
          type: new GraphQLNonNull(GraphQLString)
        }
      },
      resolve: (root, args) => {
        const newThing = new Thing({ name: args.name });
        newThing.id = newThing._id;
        return new Promise((res, rej) => {
          newThing.save(err => {
            if (err) return rej(err);
            res(newThing);
          });
        });
      }
    }
  }
});

Most helpful comment

@alexmcmillan @sibelius @RadAcademy @GlauberF @axe-z I went ahead and merged #1520.
So now you can use our npm branch as a temporary solution until we figure out how to release 14.1.0:
https://github.com/graphql/graphql-js#want-to-ride-the-bleeding-edge

All 23 comments

Background: I'm a learner. Following this tutorial I encountered this problem which was _very_ confusing trying to debug. It would be nice if there was perhaps a clearer error message (*GraphQLID* cannot represent value... (instead of just ID) would have helped me a lot). Perhaps adding an indication of how to properly cast a MongoDB ObjectID?

@alexmcmillan Thanks for detail description and especially example repo 馃憤
It should be fixed by #1520


It would be nice if there was perhaps a clearer error message (GraphQLID cannot represent value... (instead of just ID) would have helped me a lot).

Problem is that you can define GraphQL types in SDL without working directly with GraphQL* classes:

type thing {
  id: ID
  name: String
}

So we can't use GraphQLID in error message because it will confuse SDL users.

Perhaps adding an indication of how to properly cast a MongoDB ObjectID?

We can't have mongoose as a dependency so we can't detect that some object is coming from Mongo.

@IvanGoncharov that makes perfect sense, thank you for your clear explanations here. Hope your solution gets merged soon so I can come forward to v14 :)

any changes this gets into 14.0.3?

Yes, I am still stuck using '^0.13.2' as the latest '^14.0.2' continues to give me these issues.

I would also like to know if it has been fixed for the ^14.0.2,
Why am I having to use the ^0.13.2

I tested the version ^14.0.2 and returned the same problem! :(

@RadAcademy @GlauberF We plan to include it into the next release.
However since instead of depending on toString we now using toJSON it can't be a patch release. So we are working on 14.1.0 that will include this and a few other features.
We will try to release RC ASAP.

I'm a newbie at apollo, me and my aspiration .... arggh. the JS fatigue is real. Wanted to let redux go, by only using this now. here's my very small project:

server:

const express = require("express");
const mongoose = require("mongoose");
require("dotenv").config({ path: "variables.env" }); //a juste besoin du path
//const bodyParser = require('body-parser');
const { ApolloServer, gql} = require('apollo-server-express');

const cors = require("cors"); // pour que react chiasse pas en faisant des calls

mongoose
  .connect(process.env.MONGO_URI, { useNewUrlParser: true, useCreateIndex: true })  
  .then(() => console.log("Db connect茅"))
  .catch(err => console.error(err));

const app = express();

//Schema
const Blog = require('./models/Blog');
const User = require('./models/User');

// //typedefs et resolvers
const { typeDefs } = require('./schema')
const { resolvers } = require('./resolvers')



//pour react
const corsOption = {
  origin: "http://localhost:3000",
   credentials: true
};

app.use(cors(corsOption));


const port =  4444;

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: {
    Blog,
    User
  }
});

server.applyMiddleware({app});


app.listen({ port }, () =>
  console.log(`馃殌 Server ready at http://localhost:${port}${server.graphqlPath}`),
);

Blog shema
exports.typeDefs = `

type Blog {
  _id: ID
  title: String!
  author: String
  body: String!
  date: String
}
type Query {
  getAllBlogs: [Blog]
}
type Mutation {
  addBlog(title: String!, author: String, body: String!) : Blog
}
`;

resolver:

exports.resolvers = {
  Query: {
    getAllBlogs: async(root, args, { Blog }) => await Blog.find().sort({
      date: -1  //-1 = desc //1 = ascendant
    })
  },
  Mutation: {
      addBlog: async(root, { title, author, body  }, { Blog }) => {
        const newBlog = await new Blog({
          title, author, body
        }).save()
        return newBlog
      }
  },

}

in react WHERE I GET Issues with ID:

import React  from 'react';
import { Query } from "react-apollo";
import gql from "graphql-tag";

const GET_ALL_BLOGS = gql`
  query {
    getAllBlogs {
      !!!!!IF I PUT _ID I GET THE ERROR !!!!!!!!!!!!!
      title
      author
      body
      date
    }
  }
`;

const App = props => {
  return (
      <div>
        <h2>My first Apollo app </h2>
        <Query query={GET_ALL_BLOGS}>
          {({ data : { getAllBlogs } , loading, error }) => {
            if (loading) return "Loading";
            if (error) return `Error!: ${error}`;
            return  (
              <ul>
                {getAllBlogs.map(el =>  <li key={el.WOULDBE_ID}>{el.title}</li>)}
              </ul>
            );
          }}
        </Query>
      </div>
  );
};


index has the client, but it's ok, IT's that simple right now... Not sure what I'm doing wrong

@axe-z We already have a fix for this issue in #1520
We will try to release it ASAP.

oh thanks, when you are at your first steps like me , nosebleeds are ruff.

@alexmcmillan @sibelius @RadAcademy @GlauberF @axe-z I went ahead and merged #1520.
So now you can use our npm branch as a temporary solution until we figure out how to release 14.1.0:
https://github.com/graphql/graphql-js#want-to-ride-the-bleeding-edge

Your query needs to match the string type of string! not string

I had the same issue, i resolved this issue by writing this code in the types definition file of graphql

import mongoose from "mongoose";

const { ObjectId } = mongoose.Types;

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

should this be done on mongoose instead of graphql-js?

@vkarpov15 does it make sense to ObjectId implement valueOf?

@sibelius not a bad idea. Opened up a mongoose issue to track this

@vkarpov15 tks, keep the good work

still not fixed?

I don't use graphql so I'm not certain whether this is fixed, but this might help: http://thecodebarbarian.com/whats-new-in-mongoose-54-global-schematype-configuration.html

@infinitegachi try to use this mongoose PR https://github.com/Automattic/mongoose/pull/7353

I've made a field resolver to help on this

import { GraphQLNonNull, GraphQLString } from 'graphql';
import { Types } from 'mongoose';

export const mongooseIDResolver = {
  _id: {
    type: GraphQLNonNull(GraphQLString),
    description: 'mongoose _id',
    resolve: ({ _id }: { _id: Types.ObjectId}) => ({ _id: _id.toString() }),
  },
};

so if you want to expose an _id in your GraphQL, you need a custom type or transform ObjectId to string

@sibelius just FYI, Mongoose does not support import { Types } from 'mongoose' in general. It should work, but we recommend always using import mongoose from 'mongoose' to be safe because of how mongoose is structured.

Types.ObjectId is for typechecking only I guess

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5344bfc805/types/mongoose/README.md#faq-and-common-mistakes

I believe this is fixed in ^14.2.1 and mongoose ^5.4.20

Was this page helpful?
0 / 5 - 0 ratings