【问题标题】:Fragment cannot be spread here as objects of type "X" can never be of type "Y"片段不能在这里传播,因为“X”类型的对象永远不能是“Y”类型
【发布时间】:2017-11-08 01:28:30
【问题描述】:

在这种情况下,类型“X”是Application,类型“Y”是类型“Node” - 我可以看到 为什么会发生这种情况,但我对 Relay 的理解还不够了解如何修复它。 Relay 生成的查询是

query {
    node(id: $some_id) {
        ...F0
    }
}

fragment F0 on Application {
    ...
}

我有一个看起来像

的架构
query {
    application { 
        /* kind of a generic endpoint for fetching lists, etc */
        invites(token: $token) {
            name
        }
    }
    viewer { /* the current user */ }
}

我正在尝试从会话外部获取特定邀请(viewernull)。

我试过了

const application = Relay.QL`query { application }`
...
<Route ... queries={{ application }}/>
...
Relay.createContainer(Component, {
    initialValues: { token: null },
    fragments: {
        application: () => {
            fragment on Application {
                invites(token: $token) {
                    ...
                }
            }
        }
    }
})

这给了我错误

片段“F0”不能在这里传播,因为“节点”类型的对象永远不能是“应用程序”类型 - 或类似的东西。

我有点困惑,因为如果我要编写一个原始查询并直接通过 GraphQL 运行它

query {
    application {
        invites(token: "asdasdasd") {
            edges {
                node {
                    name
                }
            }
        }
    }
}

它给了我想要的东西......

在后端,我的图表定义如下

export const Application = new GraphQLObjectType({
  name: 'Application',
  fields: () => ({
    id: {
      type: GraphQLString,
      resolve: () => 'APPLICATION_ID'
    },
    invites: {
        type: InviteConnectionType,
        args: connectionArgs,
        resolve: (application, args) => {
            ...
        } 
    }
  })
})

export default new GraphQLSchema({
  query: new GraphQLObjectType({
  name: 'query',
  fields: {
    node: nodeField,
    application: {
      type: Application,
      resolve: (root, args, ctx) => {
        return Promise.resolve({})
      }
    }
  }
})

我一直在研究诸如this 和一些issues on the Relay github 之类的问题,但我不清楚应该如何实现nodeInterface

编辑:当前nodeInterface 代码的short-long 是

export const {
  nodeInterface,
  nodeField
} = nodeDefinitions(
  (globalId) => {
    const { type, id } = fromGlobalId(globalId)
    return db[type].findById(id)
  },
  (obj) => {
    const name = obj.$modelOptions.name.singular
    return types[name]
  }
)

应用程序不是数据库模型,但是,它只是一个用于获取数据的通用接口。我试过检查是否type === 'Application',并返回null(虽然我明白为什么这不起作用),返回Application(GraphQLObject),但这不起作用......不太确定去哪里从那里去。

【问题讨论】:

    标签: reactjs graphql relay


    【解决方案1】:

    要对此进行更新,我是在正确的道路上。

    当前的nodeDefinitions 我只是需要一点额外的:

    nodeDefinitions(
      (globalId) => {
        const { type, id } = fromGlobalId(globalId)
    
        if (type === 'Application') {
          return Promise.resolve(Application)
        }
    
        return db[type].findById(id)
      },
      (obj) => {
        if (obj.$modelOptions) {
          /* sequelize object */
          const name = obj.$modelOptions.name.singular
          return types[name]
        } else if (obj.name === 'Application') {
          return Application
        }
    
        return null
      }
    )
    

    我不确定这是否是最好的方法,但它似乎可以解决问题。要点是,如果我想要返回的节点类型是 Application,我返回 GraphQL 对象 - { ... name: "Application" ... },我们将在下一步中使用 name 字段(第二个回调在nodeDefinitions) 重新返回 Application。我认为你可以返回一个“自定义”对象或其他东西 - 只要你返回一些独特的东西,你可以定义一个映射到 GraphQLObject 类型(第二个回调需要),这并不重要。

    【讨论】:

      【解决方案2】:
      • 您需要为 GraphQL 自动生成唯一的全局 id 您要重新获取的类型。
      • nodeInterface 中告诉 GraphQL 如何将 id 映射到对应的 GraphQL 对象。
      • 通过给定的服务器端对象nodeInterface 标识GraphQL 类型。

      以下是应用程序的简化示例:

      // nodeInterface.
      var {nodeInterface, nodeField} = nodeDefinitions(
        (globalId) => {
          var {type, id} = fromGlobalId(globalId);
      
          // The mapping from globalId to actual object id and type.
          console.log('globalId:', id);
          console.log('type:', type);
      
          if (type === 'Application') {
            // getApplication is your db method to retrieve Application object.
            // With id you could also retrieve a specific db object.
            return getApplication();
          } else {
            return null;
          }
        },
        (obj) => {
          // Note that instanceof does an identity check on the prototype object, so it can be easily fooled.
          if (obj instanceof Application) {
            return ApplicationType;
          } else {
            return null;
          }
        },
      );
      
      // Application.
      export const ApplicationType = new GraphQLObjectType({
        name: 'Application',
        fields: () => ({
          // Auto-generated, globally defined id.
          id: globalIdField('Application'),
          _id: {
            type: GraphQLString,
            resolve: () => 'APPLICATION_ID'
          },
          invites: {
              type: InviteConnectionType,
              args: connectionArgs,
              resolve: (application, args) => {
                  ...
              } 
          }
        }),
        // Declaring nodeInterface.
        interfaces: [nodeInterface]
      });
      

      请注意,在初始获取期间,nodeInterface 甚至没有执行,因此如果 nodeInterface 没有返回任何内容,则初始获取时不会出现错误。如果这没有意义,或者您仍然在苦苦挣扎,您可以发布一个指向 repo 的链接,我会调查一下。

      【讨论】:

      • 是的,我有点想我需要实现nodeInterface,但是我不确定如何...检查我的编辑。
      • 抱歉误会了。在nodeInterface 中,您基本上需要将全局定义的 id 映射到数据对象中,以便 Relay 能够获取对象。我认为这个github.com/entria/graphql-dataloader-boilerplate/blob/… 完整示例会有所帮助。
      • 但是那些不是/技术上/具有边缘和关系的对象的东西,只是其他东西的接口——比如在这种情况下的应用程序。我已经尝试过(或多或少)我认为为应用程序实现 nodeinterface 的样子。
      • 那一定是问题,你应该返回db对象,我已经在上面编辑了我的答案。
      猜你喜欢
      • 2021-07-16
      • 2017-07-21
      • 2019-09-11
      • 1970-01-01
      • 2017-11-10
      • 2021-07-18
      • 2018-07-21
      • 1970-01-01
      相关资源
      最近更新 更多