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

Posted

技术标签:

【中文标题】如何在没有循环依赖的情况下在 GraphQL 中拆分模式?【英文标题】:How to split schema in GraphQL without having circular dependencies? 【发布时间】:2016-12-24 19:48:20 【问题描述】:

我的问题类似于javascript circular dependency in GraphQL code,但我的问题不在结构和数据库级别,而是在 javascript (ES6) 中。

我的架构定义变得太大,但我看不到在哪里可以将文件分割成碎片。根据不同的对象类型进行切割似乎是合乎逻辑的,但这会带来循环依赖,类似于这个非常简化的非工作示例:

    // -- file A.js

    import  bConnection, getBs  from 'B';

    export class A  /*...*/ ;
    export var getA = (a) =>  /*...*/ ;
    export var getAs = (array_of_as) =>  /*...*/ ;

    export var aType = new GraphQLObjectType (
      name: 'A',
      fields: () => (
        bs: 
          type: bConnection,
          /*...*/
        ,
        resolve: (a, args) => connectionFromPromisedArray (
          getBs (a.bs)
        ),
        /*...*/
      ),
      interfaces: () => [ require ('./nodeDefs').nodeInterface ],
      /*...*/
    )

    export var 
        connectionType: aConnection,
        edgeType: aEdge
       = connectionDefinitions (
        name: 'A',
        nodeType: aType
      );

    // -- file B.js

    import  aConnection, getAs  from 'A';

    export class B  /*...*/ ;
    export var getB = (b) =>  /*...*/ ;
    export var getBs = (array_of_bs) =>  /*...*/ ;

    export var bType = new GraphQLObjectType (
      name: 'B',
      fields: () => (
        as: 
          type: aConnection,
          /*...*/
        ,
        resolve: (b, args) => connectionFromPromisedArray (
          getAs (b.bs)
        ),
        /*...*/
      ),
      interfaces: () => [ require ('./nodeDefs').nodeInterface ],
      /*...*/
    )

    export var 
        connectionType: bConnection,
        edgeType: bEdge
       = connectionDefinitions (
        name: 'B',
        nodeType: bType
      );

    // -- file nodeDefs.js

    import 
      fromGlobalId,
      nodeDefinitions,
     from 'graphql-relay';

    import  A, getA, aType  from 'A'
    import  B, getB, bType  from 'B'

    export var nodeInterface, nodeField = nodeDefinitions (
      (globalId) => 
        var type, id = fromGlobalId (globalId);
        if (type === 'A') 
          return getA (id);
         else if (type === 'B') 
          return getB (id);
        
      ,
      (obj) => 
        if (obj instanceof A) 
          return aType;
         else if (obj instanceof B) 
          return bType;
        
      
    )

    // -- file schema.js

    import 
      GraphQLObjectType,
      GraphQLSchema,
     from 'graphql';

    import  nodeField  from './nodeDefs';

    var queryType = new GraphQLObjectType (
      name: 'Query',
      fields: () => (
        node: nodeField,
        /*...*/
      ),
    );

对此有通用方法或最佳实践吗?

【问题讨论】:

您找到其他解决方案了吗? 【参考方案1】:

我也有同样的问题。我认为更简洁的解决方案是使用 gruntjs concat。

grunt.initConfig(
  concat: 
    js: 
      src: ['lib/before.js', 'lib/*', 'lib/after.js'],
      dest: 'schema.js',
    
  
);

使用此配置,您可以将架构拆分为多个文件,创建最终的 schema.js。

before.js 可能是这样的:

 import 
    GraphQLObjectType,
    GraphQLInt,
    GraphQLString,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
 from 'graphql';
import db from '../models/index.js';
import Auth from '../classes/auth';

after.js 可能是这样的:

const Schema = new GraphQLSchema(
    query: Query,
    mutation: Mutation
)
export default Schema;

其他文件将包含以下对象:

const Funcionario = new GraphQLObjectType(
name: 'Funcionario',
description: 'This represent a Funcionario',
fields: () => 
    return 
        id: 
            type: GraphQLInt,
            resolve(funcionario, args) 
                return funcionario.id;
            
        ,
        CPF: 
            type: GraphQLString,
            resolve(funcionario, args) 
                return funcionario.CPF;
            
        ,
        nome: 
            type: GraphQLString,
            resolve(funcionario, args) 
                return funcionario.nome;
            
        ,
        sobrenome: 
            type: GraphQLString,
            resolve(funcionario, args) 
                return funcionario.sobrenome;
            
        ,
        sessions: 
            type: new GraphQLList(Session),
            resolve(funcionario, args) 
                return funcionario.getSessions();
            
        
    

)

【讨论】:

谢谢您,我接受您的解决方案,因为它确实解决了我的问题。我仍在试图找到一种不使用 grunt 的方法,因为仅仅因为这个问题而使用它似乎有点矫枉过正。但是如果没有别的办法,我就实现这个。 如果你想试一试 Apollo 的 graphql-tools,它会真正简化你的代码并使其更具可读性。如果您使用它,我已经编写了一个名为 schemaglue.js 的工具来解决您的确切问题。我在这里写过关于缩放和组织你的 GraphQL 代码的文章:hackernoon.com/…希望这会有所帮助。【参考方案2】:

见https://github.com/francoisa/todo/tree/master/server/graphql/types

tod​​oType.js 引用了 viewerType.js 中定义的 viewerType

viewerType.js 从 todoType 导入

【讨论】:

以上是关于如何在没有循环依赖的情况下在 GraphQL 中拆分模式?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用公用文件夹和 php artisan 的情况下在 laravel graphql-playground 中加载 schema.graphql

如何在没有任何延迟的情况下在android中循环播放音频文件?

如何在没有要映射的对象数组的情况下在 React.js 中循环和渲染元素?

Gatsby + Contentful - 如何在不重新启动服务器的情况下在本地重做 GraphQL 查询(npm run dev)?

我可以在不使用 prisma 的情况下在我的 graphQL 服务器中使用 MongoDB 吗?

如何在没有 root 用户的情况下在 Linux (CentOS) 中安装软件包并进行自动依赖处理?