cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

How to Query an Interface?

I have developed an interface which several node types are implemented with using the same syntax found here. I can not however figure out how to query that defined interface, or if that is even possible.

Using the example found in the linked document above. How would I query the "Person" interface?
When I try and query my interface I get an empty list inside of my object.

Thanks

15 REPLIES 15

William_Lyon
Graph Fellow

Could you share the query you have so far?

If you have an Actor type that implements the Person interface, then you'll want to use an Inline Fragment in the query. For example, from that sample schema getting actors for a movie would look something like:

{
  Movie {
    actors {
        ... on Actor {
          name
        }
    }
  }
}

I was trying something like below.

{
  Person{
    name
    born
  }
}

I was thinking that the interface was the parent of the node type that implements it. So you have to access the specific node type before referring to the interface? Is it possible to just query all the node types of a specific interface, like the example above?

Could you also explain the difference between "Implements" & "Extends"? I tried to use "extends" in-place of "implements" but Neo4j reject the idl.

Is there a way to query all nodes and relationships using graphQL?

Having similar issues with using interfaces.

If I query an interface, I receive the following error when trying to query one of the types on that interface

"message": "Abstract type Entity must resolve to an Object type at runtime for field Query.entities with value { uuid: \"6b270c306e6a11e8bfe8b8e85647febc\" }, received \"undefined\". Either the Entity type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.",

When I query a type that has a relationship to an in interface, I receive the following error:

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

I've tried following all the GraphQL/Apollo documentation, like adding __resolveType and __typeOf resolvers and exposing the schema & fragment types to the client cache.

Apollo guidance - Unions & interfaces

Apollo guidance - Fragment matcher

Similar issue on github

I feel like I'm losing my mind because I can't figure this one out...any ideas?

I'm getting the same error as @btroop. Has anyone figured out how to use inline fragments on interface types with neo4j-graphql-js?

Hey @smkhalsa can you share your typedefs and the query? We use a couple of interface types in the tests for neo4j-graphql-js, for example here in the typedefs

andan example query that we run in the tests looks like this:

 {
    Movie(title: "River Runs Through It, A") {
      title
      ratings {
        rating
        User {
          ... on User {
            name
            userId
          }
        }
      }
    }
  }

Could be we aren't properly handling more complex uses of inline fragments?

Hey @William_Lyon, I created a gist with a minimal reproduction of the issue:

For the interface query, it seems to fail regardless of whether or not inline fragments are used. For some reason, the value of the __resolveType resolver is not getting passed back in the response.

For example, the query:

query {
  PlaylistInterface {
    id
    name
  }
}

results in the following error:

Abstract type Playlist must resolve to an Object type at runtime for field Query.PlaylistInterface with value { name: \"My Favorite Songs\", id: \"0ae75bb2-d7cd-4914-81ea-2d57594cbdfb\" }, received \"undefined\". Either the Playlist type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.

Also, the following union query...

query {
	PlaylistUnion {
    ... on PublicPlaylist {
      id
    }
    ... on UserPlaylist {
      id
    }
  }
}

... fails with this error:

schemaType.getFields is not a function

@William_Lyon have you had a chance to look at this? Interfaces and Unions are currently blockers for me. Let me know if there is anything I can do to help.

@William_Lyon Never heard back on this. Would appreciate your thoughts. Thanks

Hey @smkhalsa - sorry for the delay. Thanks for the example, I was able to reproduce your error.

Unfortunately, we don't yet have support for union types, so I'm not surprised by the union error

But regarding the PlaylistInterface query - our initial interface implementation adds a FRAGMENT_TYPE field to the generated Cypher query by inspecting the implemented type from the GraphQL resolveinfo object, with the idea that this FRAGMENT_TYPE field is then picked up in the __resolveType resolver. To make this work, augmentSchema adds a __resolveType resolver for any interfaces that is essentially this:

const resolvers = {
  SomeInterface: {
    __resolveType(obj, context, info){
      return obj["FRAGMENT_TYPE"]; 
    },
  },
};

however, it looks like we don't check for already existing __resolveType resolvers, so this ends up overwriting the __resolveType resolvers you've implemented. This we can fix easily, in fact a workaround would be to use makeExecutableSchema and implement a Query field resolver that just delegates to neo4jgraphql:

import { makeExecutableSchema } from "apollo-server";
import { neo4jgraphql } from "neo4j-graphql-js";

...

const resolvers = {
  Query: {
   PlaylistInterface: neo4jgraphql
  },
  Playlist: {
    __resolveType: (collection) => {
      return collection.imageName ? "PublicPlaylist" : "UserPlaylist"
    }
  }
};

const schema = makeExecutableSchema({
  resolvers,
  typeDefs
});


const server = new ApolloServer({
  schema,
  resolvers,
  context: { driver }
});

I tested this out and this does result in the provided __resolveType resolver getting called, but we don't actually grab the imageName field from the database since it isn't a field on Playlist. I suppose we could add some logic to add all fields for the implemented type so they would be available in the __resolveType resolver.

It seems something happened with our FRAGMENT_TYPE approach as that doesn't appear to be working properly, and unfortunately we didn't have good tests around this so not sure what went wrong there. This will require some investigation.

I'll push a fix to check for implemented __resolveType resolvers before adding them in the augment schema step for now. That's at least a start.

Thanks for bearing with us as we fix this up, I recognize the importance of getting interface and unions working properly.

I know I'm coming in to this late, but I j
ust wanted to thank you for this explanation as it cleared a lot of things up for me. Also, I think adding logic to inject all fields into the __resolveType function is a great idea. Otherwise implementing discriminated Union types (via interfaces or unions) becomes impossible unless the user includes the discriminator field in the GQL query.

Basically, +1 for this feature.

Hi there, I'm just wondering if there has been any progress on supporting union types? Thanks!

Thanks @William_Lyon for the response. I look forward to the custom __resolveType fix.

This might be a bit complicated since we wouldn't know how deep to go. For example, what if my __resolveType resolver depends on a relation or a related node. This would be the case for UserPlaylist (i.e. does a CREATED_BY relation exist?). One solution could be to provide an optional "resolveTypeFragments" map to augmentSchema or makeAugmentedSchema that would allow users to pass in a Fragment to include for a given interface. Alternative, we could just require that any dependent fields be explicitly defined in the query for custom __resolveType implementations.

Any estimate for when we might get union types working?

I'm facing the same issue also. Any update for this issue?

I found a workaround (hacky as hell....)
After running schema = makeAugmentedSchema (...) you can add the resolveType to the object.

schema._typeMap.SomeInterface.resolveType = (collection) => {
      return collection.imageName ? "PublicPlaylist" : "UserPlaylist"
    };