73

For example, a Pet is an Animal with an owner and name.

type Animal {
  species: String
}

type Pet extends Animal {
  owner: Owner
  name: String
}
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
atkayla
  • 8,143
  • 17
  • 72
  • 132
  • Possible duplicate of [How to Inherit or Extend typeDefs in GraphQL](https://stackoverflow.com/questions/47523384/how-to-inherit-or-extend-typedefs-in-graphql) – Dan Dascalescu May 19 '19 at 05:19

4 Answers4

55

Starting with the June2018 stable version of the GraphQL spec, an Object type can extend another Object type:

Object type extensions are used to represent a type which has been extended from some original type. For example, this might be used to represent local data

In your example,

type Animal {
  species: String
}

extend type Animal {
  owner: Owner
  name: String
}

This isn't inheritance per se; you can only extend the base type, not create new types based on it. Note there is no name for the new type; the existing Animal type is extended.

The graphql.org documentation doesn't mention anything about extend, but the documentation is admittedly lackluster and being transitioned from Facebook's ownership to the Linux Foundation. The JavaScript reference implementation doesn't fully support extensions, but since you've tagged your question , you can use graphql-tools, which does:

const { graphql } = require('graphql');
const { makeExecutableSchema } = require('graphql-tools');

const typeDefs = `
  type Person {
    name: String!
  }
  extend type Person {
    salary: Int
  }  
  type Query {
    person: Person
  }
`;

const resolvers = {
  Query: {
    person: () => ({ name: "John Doe", salary: 1234 })
  }
}  

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

graphql(schema, '{ person {name salary} }').then((response) => {
  console.log(response);
});

For actual type inheritance, see the graphql-s2s library.

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
  • 5
    So, to be clear, extending a type is like pushing another value onto an existing array? Just as you can either write `const foo = [1, 2];`, or you can write `const foo = [1]; foo.push(2);` ... you can write a type def, or you can write a type def and extend it later? If so, why would anyone ever use `extend`? What's the value of splitting your definition of a type into two different places? – machineghost Sep 19 '19 at 00:15
  • 1
    @machineghost: I agree it's a crippled / immature implementation. Some reasons for using it anyway are listed in [this answer](https://stackoverflow.com/a/54948335/1269037). – Dan Dascalescu Sep 22 '19 at 03:34
  • 1
    so what's the point on doing this? I only think of a use case of extending imported or 3rd party schemas. – Sebastian Mar 05 '20 at 20:55
16

Although you cannot create sub-classes / sub-types you can do a form of inheritance using interfaces: https://graphql.org/learn/schema/#interfaces

Example from the link above:

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}
 
type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}

When querying you can specify specific fields for different implementations

hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
    ... on Human {
      totalCredits
    }
  }
dpix
  • 2,765
  • 2
  • 16
  • 25
3

This is currently not possible in GraphQL, however there is an experimental package out there that might be useful for this purpose.

https://github.com/Sydsvenskan/node-graphql-partials

See example:

partial LinkFields {
  links(
    rel: String
    type: String
  ): [Link]
}

partial DocumentFields using LinkFields {
  uuid: ID!

  # The document type, such as x-im/article
  type: String
  # If specified, then a list of the products to which this document's availability is limited
  products: [String]
  # The human readable name of the document, often used publicly to identify the document
  title: String

  # The specific path on the web page where this document is publicly available
  path: String

  # A single metadata block
  metaBlock(
    # The specific metadata block type to get
    type: String
  ): MetadataBlock
}

interface Document using DocumentFields {}

type AuthorDocument implements Document using DocumentFields {}

Which results in:

type AuthorDocument implements Document {
  links(
    rel: String
    type: String
  ): [Link]

  uuid: ID!

  # The document type, such as x-im/article
  type: String
  # If specified, then a list of the products to which this document's availability is limited
  products: [String]
  # The human readable name of the document, often used publicly to identify the document
  title: String

  # The specific path on the web page where this document is publicly available
  path: String

  # A single metadata block
  metaBlock(
    # The specific metadata block type to get
    type: String
  ): MetadataBlock
}

What you can also do, since these are just strings is to create some helper functions that modify the string and insert the necessary fields.

If you are intereseted in following the discussion on Github, you can have a look at the following issue.

https://github.com/graphql/graphql-js/issues/703

Ash Belmokadem
  • 1,437
  • 10
  • 8
2

The other answers are right to mention this is not type extension in a specialization way.

Beware, the type extension seems to have two syntaxes. According to the Apollo Federation specification, there is an @extend syntax supported by graphql-java.

In the Apollo documentation, the syntax seems to be:

extend type Animal

But according to the Federation specification, another syntax is also supported:

type Animal @key(fields: "id") @extends

Some frameworks seem to support only the @ syntax.

bdulac
  • 1,686
  • 17
  • 26