0
votes

I added another field(contacts) to my apollo graphql mutation but when retrieving the field it produces false data. Even if the matching mongodb field has real data or not, graphql returns mutliple fields with null values.

I've already tried removing __typename in the apollo client graphql options but it doesn't help. Current stack is:

reactjs 16.8.5
react-apollo 2.5.2
mongodb 3.6.11
mongoose 5.3.6
apollo-server 2.3.1 

I don't understand why it does this since I have another subdocument that is almost identical but returns correctly.

// CLIENTSIDE
// APOLLO SETUP
const cache = new InMemoryCache({
   dataIdFromObject: object => object.key || null
 })

 const AuthLink = (operation, forward) => {
   const token = cookies._uid
   operation.setContext(context => ({
     ...context,
     headers: {
       ...context.headers,
       authorization: token
     }
   }))

   return forward(operation)
 }

 const httpLink = new HttpLink({
   uri:"/graphql",
   credentials: "include"
 })

// mutation
 export const LOGIN_MUTATION = gql`
  mutation loginMutation($email: String!, $password: String!) {
    login(input: {email: $email, password: $password}) {
      token
      user {
        _id
        contacts {  // This is the subfield in question
          _id
          username
        }
        subscriptions {  // This subfield returns corretly
          _id
          title
          levels {
            _id
          }
        }
      }
      error {
        path
        message
      }
    }
  }
`

// SERVERSIDE
// APOLLO SETUP
 const baseSchema = `
   schema {
     query: Query,
     mutation: Mutation
   }
 `
 const schema = makeExecutableSchema({
   typeDefs: [
     userTypeDefs,
   ],
   resolvers: merge(
     {},
     userResolvers,
   )
 })

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

 export default new ApolloServer({
   introspection: true,
   credentials: true,
   schema,
   context: ({req, res}) => ({
     url: req.protocol + "://" + req.get("host"), 
     req
   })
 })

// USER MONGOOSE MODEL
 export const UserSchema = new mongoose.Schema(
   {
     contacts: [
       {
         type: mongoose.Schema.Types.ObjectId,
         ref: "User"
       }
     ],     
     firstName: {
       type: String
     },     
     lastName: {
       type: String
     },     
     username: {
       type: String,
       lowercase: true,
       unique: true,
       required: [true, "can't be blank"],
       match: [/^[a-zA-Z0-9]+$/, "is invalid"],
       index: true
     },

     sentRequests: [
       {
         username: {
           type: String,
           default: ""
         }
       }
     ],     
     subscriptions: [
       {
         type: mongoose.Schema.Types.ObjectId,
         ref: "Course"
       }
     ],
     password: {
       default: "",
       required: [true, "can't be blank"],
       type: String
     }
   },
   {timestamps: true}
 )

 UserSchema.plugin(uniqueValidator, {message: "is already taken."})
 export default mongoose.model("User", UserSchema)

// USER GRAPHQL SCHEMA
type User {
   _id: ID
   contacts: [User]
   email: String!
   username: String
   subscriptions: [Course]
   createdAt: String
   updatedAt: String
 }

 type AuthPayload {
   token: String
   user: User
   error: [Error]
 }

 input LoginInput {
   email: String!
   password: String!
 }

 type Mutation {
   login(input: LoginInput): AuthPayload
 }

// USER RESOLVER
 const login = async (parent, args, ctx, info) => {   
   let email = args.email

   let user = await User.findOne({email})
     .populate("subscriptions")
     .populate("contacts")
     .exec()

  if (!user) {
     arrayOfErrors.push({
       path: "email",
       message: "invalid email"
     })
   } else if (user) {

   console.log("user: ", user)


   return {
     user,
     error: arrayOfErrors
   }
 }

 Mutation: {
   login
 }
// CONSOLE LOG CLIENTSIDE
user:
  confirmed: true
  contacts: Array(3)  // returns an array of 3 items??  It's [] in mongodb
    0: {_id: null, username: null}
    1: {_id: null, username: null}
    2: {_id: null, username: null}
    length: 3
    __proto__: Array(0)
  subscriptions: Array(1)
    0: {_id: "5cd36fc1c8d1e222b99d9c58", title: "Korean Class 101 Study", 
    length: 1
    __proto__: Object
__proto__: Object

// CONSOLE LOG SERVERSIDE
POST /graphql 200 64.359 ms - -
user: {
  contacts: [ ],
  subscriptions:
   [ { _id: 5cd6f7f8212af32c75555d4b,
       subscribers: 2,
       owner: 5cd36edec8d1e222b99d9c57,
       createdAt: 2019-05-09T00:09:37.845Z,
       updatedAt: 2019-05-11T16:36:14.661Z,
       __v: 23 } ],
  password: '$2b$10$bkjiazklcoqlkJSJSAioxoAqkoajsdjnqjkiaallaadfadfp7zS',
  _id: 5cd36edec8d1e222b99d9c57,
  email: '[email protected]',
  username: 'example',
  createdAt: 2019-05-09T00:05:50.314Z,
  updatedAt: 2019-05-29T17:23:21.545Z,
  __v: 32
}

I expected an empty array to return [ ], instead graphql returns an array of 3 items with null values. Even if I add real contacts into the database it still returns the 3 items with null values. The subscriptions are returning correctly yet it is almost identical to the contacts field with the exception of being a type "Course" in the mongoose model instead of type "User".

1

1 Answers

0
votes

I doubt anyone will ever come across this type of error, but here's the solution in case you do. During development I had placed this code in my resolver for testing purposes. Removing it solved the problem.

// USER RESOLVER
...
Query: {...},

/* code to remove
User: {
  contacts: user => {
    return ["Mo", "Larry", "Curly"]
  }
}
*/ 

Mutation: {...}

A very trivial thing but having not been in this actual code for several months made it difficult to spot. I never thought the 3 stooges would ever give me so much headache.