抽象类型节点必须在运行时解析为字段 Root.node 的对象类型,值为 \"\",已收到 \"null\"。

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

我正在使用 React 和 Relay 实现搜索功能。 下面是我的 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;
       });
      }
    },
  })
});

下面是我的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)
        }
      });
  })
}

现在我想通过 $query 变量获取所有帖子 所以鉴于我是这样写的

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
              }
            }
          }
        }
      }
    `,
  },
});

在我的路线中

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,
  }]
}]

一切都在 /graphql 上进行

但是当我从网站搜索时它会生成响应错误

{
  "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
        }
      ]
    }
  ]
}

因为我的网络浏览器正在发送如下请求

请告诉我我错过了什么? 另外,如果我需要添加一些额外的信息,请告诉我。

问题可能出在您的 nodeDefinitions() 函数中。第一个回调,也命名为 idFetcher 必须 return 单个对象。但是,我在你的定义中看到你 return 一个集合

var { nodeInterface, nodeField } = nodeDefinitions(
  (globalId) => {
    var { type, id } = fromGlobalId(globalId);
    ...
    }else if (type === 'Post') {
      return getPosts(); // this should be getPost(id)
    }
);

这就是为什么您的下一个回调(称为 typeResolver 失败并且 return 将您设为 null 的原因。

var { nodeInterface, nodeField } = nodeDefinitions(
  ...
  (obj) => {
    ...
    // here you get Promise/Collection instead of single Post instance, therefore condition failed
    }else if (obj instanceof Post) {
      return postType;
    }
    return null;
  }
);

LordDave 的回答揭示了您的代码中的一个问题。正如您在他的回答中评论的那样,settingTypeall_posts 字段无效。

如果您在数据库代码中使用了 mongoose 库,我发现您的查询有问题:

Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
  if (err) {
    resolve({})
  } else {
    resolve(posts)
  }
});

根据 exec 的文档,将您的查询更改为

return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec(function(err, posts) {
  if (err) {
    resolve({})
  } else {
    resolve(posts)
  }
});

作为execreturns一个承诺,你甚至可以做到

return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec();

最后我通过创建一个新类型 'postList' 并将其定义如下

使其工作
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;
    }
  }
);

在database.js

class postList {}

postList.id = "Post_id";

export {postList}
export function getpostList(id) {
  return new postList
}

和下面的根字段

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()
      }
    },
  })
});

我 运行 在使用 InterfaceType 时遇到了这个问题,并在我的 if-elseif-else 中的专门 ObjectType 之前检查了 InterfaceType TypeResolver