Apollo-server: Apollo Server ignores toJSON methods when serializing

Created on 11 Sep 2018  路  4Comments  路  Source: apollographql/apollo-server

I'm using [email protected]. I'm using classes to define my models, and some of them have a toJSON() function defined to ensure that things are transformed appropriately, as per the JSON.stringify() specification.

apollo-server ignores the supplied function.

Simple reproduction case

import { ApolloServer, makeExecutableSchema } from 'apollo-server'

const schema = makeExecutableSchema({
  typeDefs: `
    type Query {
      myData: Data
    }

    type Data {
      foo: String!
    }
  `,
  resolvers: {
    Query: {
      myData () {
        return {
          foo: 'This should not show up',
          toJSON () {
            return {
              foo: 'This *should* show up',
            }
          },
        }
      },
    },
  },
})

const server = new ApolloServer({ schema })
server.listen(3000)

Executing the following query:

query {
  myData {
    foo
  }
}

Yields:

{
  "data": {
    "myData": {
      "foo": "This should not show up"
    }
  }
}

Instead, I would expect it to show:

{
  "data": {
    "myData": {
      "foo": "This *should* show up"
    }
  }
}

My scenario

My use case is specifically involving ObjectID from mongodb, where trying to serialize directly gives the following error:

ID cannot represent value: { _bsontype: \"ObjectID\", id: <Buffer 5b 97 b1 13 09 41 c7 a3 ef 5c 40 ad> }

I should be able to write a single toJSON() that transforms the id to a string, so that I don't get this error. As it stands, I have to write a specific resolver for each id field in each to ensure that this gets handled correctly.

has-reproduction

Most helpful comment

I actually ended up creating a new scalar type.

typeDef:

scalar ObjectID

resolver:

import { GraphQLScalarType } from 'graphql'
import { Kind } from 'graphql/language'
import { ObjectID } from 'mongodb'

export const resolvers = {
  ObjectID: new GraphQLScalarType({
    name: 'ObjectID',
    description: 'The `ObjectID` scalar type represents a [`BSON`](https://en.wikipedia.org/wiki/BSON) ID commonly used in `mongodb`.',
    serialize (_id) {
      if (_id instanceof ObjectID) {
        return _id.toHexString()
      } else if (typeof _id === 'string') {
        return _id
      } else {
        throw new Error(`${Object.getPrototypeOf(_id).constructor.name} not convertible to `)
      }
    },
    parseValue (_id) {
      if (typeof _id === 'string') {
        return ObjectID.createFromHexString(_id)
      } else {
        throw new Error(`${typeof _id} not convertible to ObjectID`)
      }
    },
    parseLiteral (ast) {
      if (ast.kind === Kind.STRING) {
        return ObjectID.createFromHexString(ast.value)
      } else {
        throw new Error(`${ast.kind} not convertible to ObjectID`)
      }
    },
  }),
}

All 4 comments

I'm using [email protected]. I'm using classes to define my models, and some of them have a toJSON() function defined to ensure that things are transformed appropriately, as per the JSON.stringify() specification.

apollo-server ignores the supplied function.

Simple reproduction case

import { ApolloServer, makeExecutableSchema } from 'apollo-server'

const schema = makeExecutableSchema({
  typeDefs: `
    type Query {
      myData: Data
    }

    type Data {
      foo: String!
    }
  `,
  resolvers: {
    Query: {
      myData () {
        return {
          foo: 'This should not show up',
          toJSON () {
            return {
              foo: 'This *should* show up',
            }
          },
        }
      },
    },
  },
})

const server = new ApolloServer({ schema })
server.listen(3000)

Executing the following query:

query {
  myData {
    foo
  }
}

Yields:

{
  "data": {
    "myData": {
      "foo": "This should not show up"
    }
  }
}

Instead, I would expect it to show:

{
  "data": {
    "myData": {
      "foo": "This *should* show up"
    }
  }
}

My scenario

My use case is specifically involving ObjectID from mongodb, where trying to serialize directly gives the following error:

ID cannot represent value: { _bsontype: \"ObjectID\", id: <Buffer 5b 97 b1 13 09 41 c7 a3 ef 5c 40 ad> }

I should be able to write a single toJSON() that transforms the id to a string, so that I don't get this error. As it stands, I have to write a specific resolver for each id field in each to ensure that this gets handled correctly.

https://github.com/apollographql/apollo-server/issues/1633#issuecomment-419916875

I actually ended up creating a new scalar type.

typeDef:

scalar ObjectID

resolver:

import { GraphQLScalarType } from 'graphql'
import { Kind } from 'graphql/language'
import { ObjectID } from 'mongodb'

export const resolvers = {
  ObjectID: new GraphQLScalarType({
    name: 'ObjectID',
    description: 'The `ObjectID` scalar type represents a [`BSON`](https://en.wikipedia.org/wiki/BSON) ID commonly used in `mongodb`.',
    serialize (_id) {
      if (_id instanceof ObjectID) {
        return _id.toHexString()
      } else if (typeof _id === 'string') {
        return _id
      } else {
        throw new Error(`${Object.getPrototypeOf(_id).constructor.name} not convertible to `)
      }
    },
    parseValue (_id) {
      if (typeof _id === 'string') {
        return ObjectID.createFromHexString(_id)
      } else {
        throw new Error(`${typeof _id} not convertible to ObjectID`)
      }
    },
    parseLiteral (ast) {
      if (ast.kind === Kind.STRING) {
        return ObjectID.createFromHexString(ast.value)
      } else {
        throw new Error(`${ast.kind} not convertible to ObjectID`)
      }
    },
  }),
}

this is not user friendly at all.

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.

Was this page helpful?
0 / 5 - 0 ratings