Neo4j-graphql-js: Custom resolveType ignored

Created on 12 Nov 2018  路  3Comments  路  Source: neo4j-graphql/neo4j-graphql-js

Referring to this sample repo: https://github.com/purplemana/neo4j-graphql-exp, there's a custom resolverType which resolves interface User implementations. The code works if the schema is not augmented. Once it is augmented, User.__resolveType resolver is not invoked anymore resulting in a GraphQL error.

To reproduce:

  1. Optional: change Neo4J credentials mentioned at: https://github.com/purplemana/neo4j-graphql-exp/blob/master/src/index.js#L12
  2. Change https://github.com/purplemana/neo4j-graphql-exp/blob/master/src/index.js#L8 to remove the call to augmentSchema.
  3. Run the code and invoke users query. Note that the results are being returned.
  4. Put the augmentSchema call back.
  5. Run the code and notice that this error shows up:

"Abstract type User must resolve to an Object type at runtime for field Query.users with value { id: "One", type: "CustomUser", fullName: "One", email: "One", enabled: true, emailVerified: true }, received "undefined". Either the User type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function."

  1. Also notice that __resolveType is not invoked this time.

Is there a different way to implement interface based types with this package?

Interface

Most helpful comment

__resolveType resolvers are now generated during the schema augmentation process based on using multiple labels in Neo4j.

Please see the docs for an example of how interfaces can be used: https://grandstack.io/docs/graphql-interface-union-types.html

All 3 comments

Running into a similar issue. Have added/removed __resolveType resolvers, as well as followed Apollo documentation for adding schema to client cache.

I've received that exact error, and I've also received the following error, depending on presence of __resolveType functions.

"Error: Resolve function for \"Organization.commonName\" returned undefined",

Apollo guidance - Unions & interfaces

Apollo guidance - Fragment matcher

Does neo4j-graphql-js support interfaces? If so, what's the guidance for making them work?

If you want to query an interface with relationships in fragment, you can query the node first to see which other label it has, and then query again with that label.

For convenience, I use the following function to query by label and id, then reuse selectionSet from the original query:

const { neo4jgraphql } = require("neo4j-graphql-js");

module.exports = async function getOneNodeWithSelections(label, id, ctx, info) {
  const rInfo = {
    schema: info.schema,
    fieldName: label,
    fieldNodes: [
      {
        kind: "Field",
        name: {
          kind: "Name",
          value: label,
        },
        arguments: [
          {
            kind: "Argument",
            name: {
              kind: "Name",
              value: "id",
            },
            value: {
              kind: "Variable",
              name: {
                kind: "Name",
                value: "id",
              },
            },
          },
        ],
        directives: [],
        selectionSet: info.fieldNodes[0].selectionSet,
      },
    ],
    returnType: label,
    parentType: "Query",
    path: "Query",
    fragments: info.fragments,
    operation: {
      kind: "OperationDefinition",
      operation: "query",
      selectionSet: {
        kind: "SelectionSet",
        selections: [
          {
            kind: "Field",
            name: {
              kind: "Name",
              value: label,
            },
            arguments: [
              {
                kind: "Argument",
                name: {
                  kind: "Name",
                  value: "id",
                },
                value: {
                  kind: "Variable",
                  name: {
                    kind: "Name",
                    value: "id",
                  },
                },
              },
            ],
            directives: [],
            selectionSet: info.fieldNodes[0].selectionSet,
          },
        ],
      },
    },
  };

  return neo4jgraphql({}, { id }, ctx, rInfo);
};

And you can write the interface resolver as follow, (given Node is the interface name)

exports.Query = {
  async Node(_, { id }, { driver }, info) {
    const query = /* cypher */ `
    MATCH (n:Node {id: $id}) RETURN labels(n) as labels;
    `;

    const session = driver.session();
    const resp = await session.run(query, { id });
    const [record] = resp.records;

    if (!record) {
      return null;
    }

    const labels = record.get("labels");
    const FRAGMENT_TYPE = labels.find(l => l !== "Node");

    const r = await getOneNodeWithSelections(
      FRAGMENT_TYPE,
      id,
      { driver },
      info
    );
    return {
      ...r,
      FRAGMENT_TYPE, // this is important because neo4j-graphql uses this attr to resolve type by default
    };
  },
};

This takes 2 queries for one node. However, if you have only one fragment spread, you can probably use that fragment condition to eliminate the first query.

__resolveType resolvers are now generated during the schema augmentation process based on using multiple labels in Neo4j.

Please see the docs for an example of how interfaces can be used: https://grandstack.io/docs/graphql-interface-union-types.html

Was this page helpful?
0 / 5 - 0 ratings