Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split schema in GraphQL without having circular dependencies?

My question is similar to Javascript circular dependency in GraphQL code but my problem is not on the structure and database level, but in javascript (ES6).

My schema definition is getting to grow too large but I don't see where I could cut the file into pieces. It seems to be logical to cut based on the different object types, but that brings to circular dependencies similarly to this very much simplified, non-working example:

    // -- 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,
        /*...*/
      }),
    });

Is there a common way or best practice for this?

like image 865
Gergely Borgulya Avatar asked Aug 18 '16 01:08

Gergely Borgulya


1 Answers

I've the same problem. I think the cleaner solutions is about use gruntjs concat.

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

With this config, you can split your schema in several files, creating a final schema.js.

before.js may be this way:

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

after.js may be this way:

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

The others files will contains Objects like:

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();
            }
        }
    }
}
})
like image 160
José Manuel Villar Avatar answered Nov 04 '22 08:11

José Manuel Villar