Search code examples
node.jsreactjsrelayjs

Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\"."


I am implementing a search feature with react and relay. Below is my schema.js

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

var postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    _id: {
      type: new GraphQLNonNull(GraphQLID)
    },
    createdAt: {
      type: GraphQLString
    },
    id: globalIdField('Post'),
    title: {
      type: GraphQLString
    },
    color: {
      type: GraphQLString
    },
    userId: globalIdField('User'),
    username: {
      type: GraphQLString,
      resolve: (post) => getUserById(post.userId),
    },
    content: {
      type: GraphQLString
    },
    images: {
      type: postImageType,
      description: "Post's main image links"
    }
  },
  interfaces: [nodeInterface]
});
const {
  connectionType: postConnection,
} = connectionDefinitions({name: 'Post', nodeType: postType});

var settingType = new GraphQLObjectType({
  name: 'Setting',
  fields: {
    _id: {
      type: new GraphQLNonNull(GraphQLID)
    },
    id: globalIdField('Setting'),
    amount: {
      type: GraphQLString
    },
    all_posts: {
      type: postConnection,
      args: {
       ...connectionArgs,
        query: {type: GraphQLString}
      },
      resolve: (rootValue, args) => connectionFromPromisedArray(
        getAllPosts(rootValue, args),
        args
      ),
    },
  },
  interfaces: [nodeInterface]
});

var Root = new GraphQLObjectType({
  name: 'Root',
  fields: () => ({
    node: nodeField,
    setting: {
      type: settingType,
      args: {
         ...connectionArgs,
          currency: {type: GraphQLString}
        },
      resolve: (rootValue, args) => {
       return getSetting(args.currency).then(function(data){
        return data[0];
       }).then(null,function(err){
        return err;
       });
      }
    },
  })
});

Below is my database.js

export function getAllPosts(params,args) {
  let findTitle = {};
  let findContent = {};
  if (args.query) {
    findTitle.title = new RegExp(args.query, 'i');
    findContent.content = new RegExp(args.query, 'i');
  }
  console.log("getAllPosts",args)
  return new Promise((resolve, reject) => {
      Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
        if (err) {
          resolve({})
        } else {
          resolve(posts)
        }
      });
  })
}

Now I want to fetch all posts by $query variable So in view I wrote like this

import React, { Component } from 'react';
import Relay from 'react-relay';

class BlogList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      query: '',
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(){
    this.props.relay.setVariables({query: this.state.query});
  }
  render() {
    return (

        <div className="input-group col-md-12">
            <input type="text" onChange={this.handleChange.bind(this,"query")} value={this.state.query} name="query" placeholder="Enter Title or content"/><br/>

            <span className="input-group-btn">
                <button type="button" onClick={this.handleSubmit} className="btn btn-info btn-lg">
                    <i className="glyphicon glyphicon-search"></i>
                </button>
            </span>
        </div>
    )
  }
};

export default Relay.createContainer(BlogList, {
  initialVariables: {
    query: ''
  },
  fragments: {
    viewer: () => Relay.QL`
      fragment on Setting {
        id,
        all_posts(first: 10000000,query: $query) {
          edges {
            node {
              id,
              _id,
              title,
              content,
              createdAt,
              username,
              color,
              images{
                full
              }
            }
          }
        }
      }
    `,
  },
});

And in routes I have

const SettingQueries = {
 viewer: () => Relay.QL`query{
  setting(currency: "USD")
 }`,
}
export default [{
  path: '/',
  component: App,
  queries: UserQueries,PostQueries,SettingQueries,
  indexRoute: {
    component: IndexBody,
  },
  childRoutes: [ 
  ,{
    path: 'settings',
    component: Setting,
    queries: SettingQueries,
  }]
}]

Things are working on /graphql as Graphql UI

but when I search from website it generates error in response

{
  "data": {
    "node": null
  },
  "errors": [
    {
      "message": "Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\".",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ]
    }
  ]
}

as my web-browser is sending requests as below enter image description here

Please suggest me what am I missing? Also If I need to add some additional information please let me know.


Solution

  • Finally I got it working by creating a new type 'postList' and defined it as below

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

    In database.js

    class postList {}
    
    postList.id = "Post_id";
    
    export {postList}
    export function getpostList(id) {
      return new postList
    }
    

    and under root fields as below

    var postListType = new GraphQLObjectType({
      name: 'postList',
      description: 'List of posts',
      fields: () => ({
        id: globalIdField('postList'),
        posts: {
          type: postConnection,
          description: 'List of posts',
          args: {
           ...connectionArgs,
            query: {type: GraphQLString}
          },
          resolve: (_, args) => connectionFromPromisedArray(getAllPosts(_,args), args),
        },
      }),
      interfaces: [nodeInterface],
    });
    
    var Root = new GraphQLObjectType({
      name: 'Root',
      fields: () => ({
        node: nodeField,
        postList: {
          type: postListType,
          resolve:(rootValue)=> {
            return getpostList()
          }
        },
      })
    });