node.jsreactjsgraphqlrelayreact-router-relay

Cannot read property 'after' of undefined in implementing react-relay


In my nodejs application

here is my schema.js file

import {
  GraphQLBoolean,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} from 'graphql';

import {
  connectionArgs,
  connectionDefinitions,
  connectionFromArray,
  connectionFromPromisedArray,
  cursorForObjectInConnection,
  fromGlobalId,
  globalIdField,
  mutationWithClientMutationId,
  nodeDefinitions,
  toGlobalId,
} from 'graphql-relay';

import {
  User,
  getUser,
  getPosts,
  createpost,
} from '../data/database.js';

var { nodeInterface, nodeField } = nodeDefinitions(
  (globalId) => {
    var { type, id } = fromGlobalId(globalId);
    if (type === 'User') {
      return getUser(id);
    }else if (type === 'Post') {
      return getPosts(id);
    }
    return null;
  },
  (obj) => {
    if (obj instanceof User) {
      return userType;
    }else if (obj instanceof Post) {
      return postType;
    }
    return null;
  }
);

var userProfileImageType = new GraphQLObjectType({
  name: 'ProfileImage',
  fields: {
    full: {
      type: GraphQLString
    }
  }
});

var userLocalAccountType = new GraphQLObjectType({
  name: 'Local',
  fields: {
    email: {
      type: GraphQLString
    }
  }
});

var userFacebookAccountType = new GraphQLObjectType({
  name: 'Facebook',
  fields: {
    id: {
      type: GraphQLString
    },
    displayName: {
      type: GraphQLString
    }
  }
});

var userGoogleAccountType = new GraphQLObjectType({
  name: 'Google',
  fields: {
    id: {
      type: GraphQLString
    },
    displayName: {
      type: GraphQLString
    }
  }
});

var postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    id: globalIdField('Post'),
    title: {
      type: GraphQLString
    },
    userId: globalIdField('User'),
    content: {
      type: GraphQLString
    }
  },
  interfaces: [nodeInterface]
});

/**
 * Define your own connection types here
 */

const {
  connectionType: postConnection,
  edgeType: postEdge,
} = connectionDefinitions({name: 'Post', nodeType: postType});

let createPostMutation = mutationWithClientMutationId({
    name: 'CreatePost',

    inputFields: {
      title: { type: new GraphQLNonNull(GraphQLString) },
      content: { type: new GraphQLNonNull(GraphQLString) },
      userId: { type: new GraphQLNonNull(GraphQLString) },
    },

    outputFields: {
      postEdge: {
        type: postConnection,
        resolve: (obj) => ({ node: obj, cursor: obj.insertedId })
      },
    },

    mutateAndGetPayload: ({title, content,userId}) => {
      console.log({title, content,userId})
      return createpost({title, content,userId})
    }
  });

var userType = new GraphQLObjectType({
  name: 'User',
  fields: {
    id: globalIdField('User'),
    fullName: {
      type: GraphQLString,
      description: "Users' Full Name"
    },
    isPremium: {
      type: GraphQLBoolean,
      description: "Does the user have premium subscription?"
    },
    currentPostCount: {
      type: GraphQLInt,
      description: "User's current total post"
    },
    images: {
      type: userProfileImageType,
      description: "User's profile image links"
    },
    local: {
      type: userLocalAccountType,
      description: "User's local account info"
    },
    facebook: {
      type: userFacebookAccountType,
      description: "User's Facebook account info"
    },
    google: {
      type: userGoogleAccountType,
      description: "User's Google Plus account info"
    },
    posts: {
      type: postConnection,
      args: {
        ...connectionArgs,
        query: { type: GraphQLString }
      },
      resolve: (rootValue) => {
        return connectionFromPromisedArray(getPosts(rootValue))
      }
    },
  },
  interfaces: [nodeInterface]
});

var Root = new GraphQLObjectType({
  name: 'Root',
  fields: () => ({
    user: {
      type: userType,
      resolve: (rootValue, _) => {
        return getUser(rootValue)
      }
    },
  })
})

export var schema = new GraphQLSchema({
  query: Root,
  mutation: new GraphQLObjectType({
    name: 'Mutation',
    fields: () => ({
      createPost: createPostMutation
    })
  })
});

and below is my database.js

import User from './models/userModel'
import Post from './models/postModel'

export { User }
export { Post }

export function getUser(user) {
  let validUser = false
  if (user.local || user.facebook || user.google) {
    validUser = true
  }
  return new Promise((resolve, reject) => {
    if (validUser) {
      let localEmail = user.local.email || ""
      let googleID = user.google.id || ""
      let facebookID = user.facebook.id || ""
      User.findOne({
        $or: [
          { "local.email": localEmail },
          { "facebook.id": facebookID },
          { "google.id": googleID }
        ]
      }, function(err, existingUser) {
        if (err || !existingUser) {
          resolve({})
        } else {
          resolve(existingUser)
        }
      });
    } else {
      resolve({})
    }
  })
}

export function getPosts(user) {
  return new Promise((resolve, reject) => {
     Post.find({}).exec({}, function(err, posts) {
        resolve(posts)
      });

  })
}

export function createpost({title, content,userId}){
  return new Promise((resolve, reject) => {
      const blogPostModel = new Post({title, content,userId});
      const newBlogPost = blogPostModel.save();
      if (!newBlogPost) {
        throw new Error('Error adding new blog post');
      }
       resolve({})
  })
}

I am getting user by

Relay.QL `query { user}`

I want to fetch all posts along with user

Relay.QL 'query{
  user {
    id,
    posts(first: 3) {
      edges {
        node {
          id,
          title
        }
      }
    }
  }
}'

but i am getting below error in terminal

    Failed to execute query `UserQueries` for the following reasons:

1. Cannot read property 'after' of undefined
   le {id,displayName},_posts3KBCho:posts(first:3) {edges {node

Please help. Thanks in Advance.


Solution

  • Update #1:

    The problem is in the implementation of posts field in userType definition.

    posts: {
      type: postConnection,
      args: {
        ...connectionArgs,
        query: { type: GraphQLString }
      },
      resolve: (rootValue) => {
        return connectionFromPromisedArray(getPosts(rootValue))
      }
    },
    

    While destructuring args, the Rest parameter connectionArgs should come in the end. query should come first. Because of the wrong use of rest parameters, connectionArgs is found undefined.

    By the way, you don't pass query for posts. If you don't provide query argument, you should just use args: {...connectionArgs},.


    Looks like you've used Mongoose library. If that's the case, one obvious problem is in the implementation of getPosts function. exec itself returns a promise. So, it doesn't need to be wrapped in a Promise. Replace this

    export function getPosts(user) {
      return new Promise((resolve, reject) => {
         Post.find({}).exec({}, function(err, posts) {
            resolve(posts)
          });
    
      })
    }
    

    with this

    export function getPosts(user) {
        return Post.find({user_id: user._id}).exec();
    }
    

    Please let me know if you face further problems after fixing this.