GraphQL 循环依赖

Posted

技术标签:

【中文标题】GraphQL 循环依赖【英文标题】:GraphQL circular dependency 【发布时间】:2017-07-20 18:09:10 【问题描述】:

我是 javascript 的新手,目前正在学习使用 Node.js 实现一个带有 MongoDB 后端的 graphQL API。我遇到了两种类型之间循环依赖的问题。

基本上,我有一个经典的博客文章/博客作者情况。一篇文章只有一个作者,因此 mongoose 模式包含对该作者的引用。

在我的 graphQL 类型“作者”中,我想添加一个字段“帖子”,它允许我从作者导航到他们撰写的所有帖子。该引用未在数据库模型中编码,而是通过控制器检索。这是我的博文代码。

var graphql = require("graphql");
var AuthorResolvers = require("../resolvers/author");
var PostResolvers = require("../resolvers/post");
var AuthorType = require("./author").AuthorType;

var PostType = new graphql.GraphQLObjectType(
  name: "PostType",
  fields: 
        _id: 
            type: graphql.GraphQLID
        ,
        title: 
            type: graphql.GraphQLString
        ,
        author: 
            type: AuthorType,
            description: "Author of this post",
            resolve: (post) => AuthorResolvers.retwArgs(post)
        
    
);

module.exports = PostType;

resolvers.js 文件仅导出用于寻址控制器的函数。

我的作者类型定义如下:

var graphql = require ("graphql");
var AuthorResolvers = require("../resolvers/author");
var PostResolvers = require("../resolvers/post");

var AuthorType = new graphql.GraphQLObjectType(
  name: "AuthorType",
  fields: () => 
        var PostType = require("./post");
        return 
            _id: 
                type: graphql.GraphQLID
            ,
            name: 
                type: graphql.GraphQLString
            ,
            posts: 
                type: new graphql.GraphQLList(PostType),
                description: "Posts written by this author",
                resolve: (author) => PostResolvers.retwArgs(author)
            
        
    
);

这里有两件事我已经尝试过了:

    我使用了一个函数来返回 fields 字段。我认为这被称为 thunk 或闭包。 我需要在返回字段的函数中使用 PostType。当我需要文件 ./post 和其他需要时,错误已经被抛出在文件的顶部。

当我尝试运行此服务器示例时,我收到错误:

Can only create List of a GraphQLType but got: [object Object].

指向线

type: new graphql.GraphQLList(PostType)

在作者中。

包含上面发布的类型定义的文件也会像这样导出查询:

var PostQuery = 
    posts: 
        type: new graphql.GraphQLList(PostType),
        description: "Posts of this Blog",
        resolve: PostResolvers.ret
    
;

为帖子点赞

var AuthorQuery = 
    authors: 
        type: new graphql.GraphQLList(AuthorType),
        description: "The authors working on this Blog",
        resolve: AuthorResolvers.ret
    
;

分别为作者。一切都集中在一个 Schema 文件中,如下所示:

var graphql = require("graphql");
var Author = require("./types/author").AuthorQuery;
var Post = require("./types/post").PostQuery;

var root_query = new graphql.GraphQLObjectType(  
  name: "root_query",
  fields: 
    posts: Post.posts,
    authors: Author.authors
  
);

module.exports = new graphql.GraphQLSchema(
  query: root_query
);

最后是服务器:

var graphql = require ('graphql').graphql  
var express = require('express')  
var graphQLHTTP = require('express-graphql')
var Schema = require('./api/schema')

var app = express()
  .use("/", graphQLHTTP(
    
      schema: Schema,
      pretty: true,
      graphiql: true,
    
  ))
  .listen(4000, function(err) 
    console.log('Running a GraphQL API server at localhost:4000');
  )

我真的没有办法解决这种循环依赖。如果我只是在 AuthorType 定义中注释掉对 PostType 的引用,则服务器启动没有问题。在这里的任何帮助将不胜感激。

也许是为了更好地理解。目录结构如下:

│   package.json
│   server.js
│
├───api
│   │   schema.js
│   │
│   ├───controllers
│   │       author.js
│   │       post.js
│   │
│   ├───models
│   │       author.js
│   │       post.js
│   │
│   ├───resolvers
│   │       author.js
│   │       post.js
│   │
│   └───types
│           author.js
│           post.js
│
└───config
        db.js

【问题讨论】:

如果你尝试定义像fields: () => ( _id: ..., author: ... )这样的字段会发生什么? 然后我必须将var PostType = require("./post"); 放在文件的顶部,而不是在不同的类中得到错误。输出然后是PostType.author field type must be Output Type but got: undefined. 并且奇怪地指向了 GraphQL 模式的生成。 如果我随后添加另一个函数以使用您定义的函数检索 PostType 中的字段,则会引发相同的错误。 【参考方案1】:

这是一个比较典型的模块循环依赖问题——你可以参考这个问题How to deal with cyclic dependencies in Node.js。在这种情况下,它与 GraphQL 无关。

更重要的是,你执行module.exports = PostType 但是在AuthorType 执行var PostType = require('./post'),不应该是var PostType = require('./post').PostType 吗?我想这是你得到以下错误的原因:

Can only create List of a GraphQLType but got: [object Object].

因为您的 PostType 现在是 PostType: ... 而不是 GraphQLObjectType 实例。

【讨论】:

非常感谢。 PostType 确实是我固定的类型。不过,这不是问题。您的链接使我得到了正确的答案,即:确保在任何 require 语句导入任何内容之前定义您的导出。 (我在解释)非常感谢。【参考方案2】:

piotrbienias 的回答让我找到了正确的线索。我以前读过它,但没有完全理解。在需要类之前定义导出是必要的。就我而言,我能够像这样修复它:

module.exports.AuthorType = new graphql.GraphQLObjectType(
  name: "AuthorType",
  fields: () => 
        var PostType = require("./post").PostType;
        return 
            _id: 
                type: graphql.GraphQLID
            ,
            name: 
                type: graphql.GraphQLString
            ,
            posts: 
                type: new graphql.GraphQLList(PostType),
                description: "Posts written by this author",
                resolve: (author) => PostResolvers.retwArgs(author)
            
        
    
);

对于 PostType 也是如此。这样,导出是在调用 require 之前定义的。

非常感谢!

【讨论】:

这是我的解决方案——关键是为fields 参数使用回调函数并将你的require() 放入该函数中 这是一种称为“thunk”的模式。【参考方案3】:

根据this articlegraphQL 确实在一定程度上增加了循环依赖的问题。文章建议使用extend type 来定义相互依赖的类型。它在我的情况下有效。

所以不要以“直截了当”的方式定义类型:

type User 
    id: ID
    posts: [Post]

type Post 
    id: ID
    user: User

您应该使用单向定义,然后使用 extend 添加另一个依赖方向,如下所示:

type User 
    id: ID 
    // no reference to [Post] !

type Post 
    id: ID
    user: User // this stays - single direction

extend type User 
    posts: [Post]

有关理由和更详细的示例,请参阅文章。

【讨论】:

以上是关于GraphQL 循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

graphql-java 循环类型依赖

在 Nestjs/GraphQL 中使用接口时避免循环依赖

如何在没有循环依赖的情况下在 GraphQL 中拆分模式?

如何使用 GraphQL Relay 连接修复循环依赖

在客户端解析器中导入类型时,如何避免使用 Apollo 客户端和“graphql-codegen”的角度项目中的循环依赖?

Spring的循环依赖问题