I am new to 'GraphQL' using nodejs. I am stucked into bi-directional schema mapping. posts <-> authors. Using graphql and graphql-relay module.

Following are the two schema we are using.

--posts.js // here we are requiring authors.js 
const {
    AuthorType,
    schema: AuthorSchema,
    AuthorsConnection
} = require('./authors');

class Post {}

const {
    nodeInterface,
    nodeField
} = nodeDefinitions(
    globalId => {
        const {
            type,
            id
        } = fromGlobalId(globalId);
        // return based on the id
        return DataSource['posts'][0];
    },
    obj => {
        console.log(" : PostType : ", PostType);
        // type to be return 
        return Post;
    }
);

const PostType = new GraphQLObjectType({
    "name": "PostType",
    "description": "Posts type and it's relevant fields",
    "fields": () => ({
        "id": globalIdField('Post'),
        "title": {
            "type": GraphQLString
        },
        "body": {
            "type": GraphQLString
        },
        "author": {
            "type": AuthorsConnection,
            "resolve": (parent, argument, root, currentSdl) => {
                console.log("v1, v2, v3, v4  :", parent);
                if (parent.author)
                    return connectionFromArray(DataSource['authors'], {})
                return [];
            }
        }
    }),
    isTypeOf: Post,
    interfaces: [nodeInterface]
});

const {
    connectionType: PostsConnection,
    edgeType: GQLPostEdge
} = connectionDefinitions({
    name: "Post",
    nodeType: PostType
});
module.exports = exports = {
    PostType,
    PostsConnection,
    schema: {
        post: nodeField,
        posts: {
            type: PostsConnection,
            resolve: (root, v2, v3) => {
                return connectionFromArray(DataSource['posts'], {});
            }
        }
    }
};

--authors.js // here we have required posts.js

const {
    PostType,
    PostsConnection
} = require('./posts');

class Author {}

const {
    nodeInterface,
    nodeField
} = nodeDefinitions(
    globalId => {
        const {
            type,
            id
        } = fromGlobalId(globalId);
        // return based on the id
        return DataSource['authors'][0];
    },
    obj => {
        console.log(" : Authorype : ", Authorype);
        // type to be return 
        return Author;
    }
);

const AuthorType = new GraphQLObjectType({
    "name": "AuthorType",
    "description": "Author type and it's relevant fields",
    "fields": () => ({
        "id": globalIdField('Author'),
        "firstName": {
            "type": GraphQLString
        },
        "lastName": {
            "type": GraphQLString
        },
        authorPosts: {
            type: PostsConnection,
            resolve: (parent, args, root, context) => {
                return connectionFromArray(DataSource['posts'], {});
            }
        }
    }),
    isTypeOf: null,
    interfaces: [nodeInterface]
});

const {
    connectionType: AuthorsConnection,
    edgeType: GQLAuthorEdge
} = connectionDefinitions({
    name: "Author",
    nodeType: AuthorType
});

module.exports = exports = {
    AuthorType,
    AuthorsConnection,
    schema: {
        author: nodeField,
        authors: {
            type: AuthorsConnection,
            resolve: (root, v2, v3) => {
                return connectionFromArray(DataSource['authors'], {});
            }
        }
    }
};

Once I merge above schema for GraphQL I am getting following error.

Error: Schema must contain unique named types but contains multiple types named "Node".

I tried to debugged this issue, following is I observed following.

  • Once I change "authors" field from posts schema to other than "AuthorsConnection" it starts working.
  • Or if removed "authors" field from posts schema it starts working.

Please let me know what is issue here, is it relevant to nodeDefinitions function?

2

There are 2 best solutions below

2
Benjie On BEST ANSWER

It is indeed related to the nodeDefinitions function. From the graphql-relay docs:

nodeDefinitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.

You're calling this twice, which is resulting in the Node type being defined twice, and you're referencing one of each:

schema: {
    post: nodeField,

// ...

schema: {
    author: nodeField,

This is causing the error - there's now two independent instances of Node which is invalid.

The solution is to only call nodeDefinitions once, and then pass the reference to the generated nodeField and nodeInterface to the relevant places. Then your globalId => {...} function will need to look at the type to figure out how to get the relevant record, be it an author or a post.

0
HDB On

Along with above answer given by @Benjie. I find out the way to overcome issues which was resulting into error of Error: Schema must contain unique named types but contains multiple types named "Node"..

Following are the key points to be check when we are making graphql in modular way.

  • Don't create new instances of type, For eg: const PostType = new GraphQLObjectType({}) it should always send single object rather than new object every time.
  • use nodeDefinations only once.
  • Check for the cyclic issues in common javascript issue which will occurs.

Thanks.