Add an Interface to a GraphQL Schema

Josh Black
InstructorJosh Black

Share this video with your friends

Send Tweet
Published 5 years ago
Updated 3 years ago

As we start building out more complex GraphQL schemas, certain fields start to repeat across different types. This is a perfect use-case for the Interface Type made available to us through GraphQL’s Type System. In this video, we’ll go over how to create an Interface Type and how to add it to an existing type in a GraphQL Schema.

[00:00] Currently in our GraphQL schema, we have a video type. This video type has a field called ID. This field is of type GraphQL ID and it's interesting, because if we were to add another type to our GraphQL schema, such as an instructor type, we might also want that instructor type to have the same field.

[00:22] When we want to describe a set of possible fields that a type could have, we would use something called a GraphQL interface type. To create this interface, let's go and create a Node.js file inside of source/node.js.

[00:39] Next, we'll open that file and start importing a couple of things. First, we'll make sure to grab a GraphQL interface type since we are creating an interface.

[00:49] We'll also grab GraphQL a non-null and GraphQL ID to describe the ID field on this interface. Then, we'll just require all of these from the GraphQL package.

[01:02] Next up, we'll define our actual interface, which we're calling "Node Interface," and this is just a new GraphQL interface type. Inside of the constructor, we can give it a name, in this case Node.

[01:14] Then, we'll also describe all of the fields that are available on types that use this interface, in this case, we will define ID. The type of this ID will be a new GraphQL non-null GraphQL ID. The last thing that we're going to do is just export our interface using module to export, and then passing in Node interface.

[01:42] Now, we can switch back into our index.js file and import this Node interface at the bottom of our definitions here. We'll do, "Const nodeinterface=require," and then, we'll pass in /source/node.

[01:58] Now, we can go find our video type which is down here, and we can add in our interfaces at the bottom of the definition here. We'll say interfaces and pass in an array.

[02:09] We'll just add in that Node interface as the first part of that array of interfaces. The goal for this interface is to be able to use it anytime we have shared fields between types.

[02:23] Say, we were defining an instructor type which would be a new GraphQL object type. Then inside of here, we knew that the fields for this would also contain this ID field, as well.

[02:37] Since we're sharing these fields, we would also add the interfaces array once again and pass in the Node interface as the first element of that array.

[02:48] This is guaranteeing that these two types that share the same fields now have a common interface that they both implement. Let's get rid of this instructor type, since we are not actually using it and try and run our current server using Node index.js.

[03:06] What we get is actually an error, because our interface type Node doesn't actually provide a way for that interface to know what type it's working with when we're trying to resolve that field.

[03:19] Let's go and fix this error by switching back into our text editor of choice, and then going to source/node.js. Inside of here, we're going to add a method called Result type. This is going to be a function that takes in an object.

[03:34] What we're going to do is check to see if this object has a title, then we're going to return video type. We'll import that in a second, otherwise, we don't know what type it is, we'll just return null.

[03:48] To get this video type, we need to make sure that we're exporting it. Inside of index.js at the end of our video type definition, and it's going to do exports.videotype=videotype.

[04:00] Then, we'll switch back into Node.js and import at the top. Const videotype is going to be equal to require, and then, we'll just put in the path to the index.js file.

[04:19] Now that we've imported our video type and we've made sure to add a resolve type method, let's go and try to rerun our server to see if there's any more issues.

[04:28] We'll do Node index.js. We see that we have a new error here, but here, we're just saying that node.id is expecting the non-null GraphQL ID type, but what we're putting in the video type right now is just a GraphQL ID. This is just saying that our given type video is not actually following the interface that it is implementing.

[04:52] To fix this, let's hop back into our index.js file and instead of saying that the video type has a field that just has a type for GraphQL ID. We're going to say, "New GraphQL non-null," to make sure that it matches our interface. We'll pass in GraphQL ID.

[05:10] Now with this new type definition, let's go back to our server and try and run it. It looks like we're good to go.

Maxim Kazantsev
Maxim Kazantsev
~ 5 years ago

Great course Josh, really enjoying it, thanks a lot for putting all of it together. One small thing, not related to GraphQL, is that in this video you introduce a circular dependency (index.js depends on node.js and vice versa). Which means you get tightly coupled modules that goes against the concept of modularity. So the most naive option to work around that issue I guess would be to define that resolveType() function in index.js and pass it on to a function in node.js, say, getNodeInterface() that will set resolveType to the passed function. Defining the interface in index.js is also an option.

Keep those lessons coming! 😁

Scott Spence
Scott Spence
~ 4 years ago

I've lost it by this point, why would you demonstrate a way to do something then say, we're not going to do it that way, we're doing it this way instead, I was coding along and have lost the thread of whats going on, pretty frustrated with it now.

@02:22 you spend some time defining an instructor type, then get rid of it cause "we're not actually using it"?

Why define it in the first place?

ajando
ajando
~ 4 years ago

@Sébastien thanks for expressing the question in such an eloquent manner, its exactly what I would ask.

@Josh Thanks for replying, you made it very clear in your comment. It would be grate if you somehow point to your answer in the comment from the video or description.

Byron McMullen
Byron McMullen
~ 3 years ago

When you introduce validation errors with the interface you get errors when the server initializes. I'm only getting those in Graphiql. Is that just a change between versions (I'm on 14.0.2)?

Zac Jones
Zac Jones
~ 3 years ago

Hey @Bryon, have you compared your code example to the lesson code (it has recently been updated) and works for me on 14.0.2.

David
David
~ 3 years ago

When you introduce validation errors with the interface you get errors when the server initializes. I'm only getting those in Graphiql. Is that just a change between versions (I'm on 14.0.2)?

I too get this same problem; the validation error only appears in GraphiQL, even when cloning his code from Github.

Eleonora Lester
Eleonora Lester
~ 3 years ago

I didn't actually get the point of using interfaces until I read the documentation and it gives you some pretty cool example.

https://graphql.org/learn/schema/

Interfaces are useful when you want to return an object or set of objects, but those might be of several different types.
~ 4 months ago

I also got the circular dependecy problem. Tried putting everything in the same file, which doesn't throw a javascript error, but graphql complains because the type is undefined. I tried playing with the putting resolveType as a function that is hoisted but no luck.

const nodeInterface = new GraphQLInterfaceType({
  name: 'Node',
  fields: {
    id: new GraphQLNonNull(GraphQLID),
  },
  resolveType: resolveType,
});

const videoType = new GraphQLObjectType({
  name: 'Video',
  description: 'A video on Egghead.io',
  fields: {
    id: {
      type: new GraphQLNonNull(GraphQLID),
      description: 'The id of the video',
    },
    title: {
      type: GraphQLString,
      description: 'The title of the video',
    },
    duration: {
      type: GraphQLInt,
      desciption: 'The duration of the video (in seconds)',
    },
    released: {
      type: GraphQLBoolean,
      desciption: 'Whether or not the viewer has released the video',
    },
  },
  interfaces: [nodeInterface],
});

var resolveType = function (object) {
  if (object.title) {
    return videoType;
  }
  return null;
};
query myFirstQuery {
  videos {
    id
    title
  }
}
{
  "errors": [
    {
      "message": "Interface field Node.id expects type undefined but Video.id is type ID!."
    },
    {
      "message": "The type of Node.id must be Output Type but got: undefined."
    }
  ]
}