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:
augmentSchema.users query. Note that the results are being returned.augmentSchema call back."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."
__resolveType is not invoked this time.Is there a different way to implement interface based types with this package?
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
Most helpful comment
__resolveTyperesolvers 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