Table of Contents

Interface

interface Person {
  name: String!
}

Interfaces allow you to set fields and their respective types and settings that must be equivalent in all objects that implement these interfaces. You can think of interfaces as inheritance, where objects can inherit from multiple interfaces. Unlike unions, interfaces can have fields.

Creating an Interface

You can define interfaces on a file or using the shortcut on the schema.

# app/graphql/interfaces/person.rb
module GraphQL
  class Person < GraphQL::Interface
    # Any object that implements this interface must have an equivalent field
    field :email
  end
end

# OR

# app/graphql/app_schema.rb
interface 'Person' do
  field :email
end

Read more about field lists.

Description

Allows documenting interfaces. This value can be retrieved using introspection or during a to_gql output. Within the class, desc works as a syntax sugar for self.description = ''. It also supports descriptions from I18n.

# app/graphql/interfaces/person.rb
module GraphQL
  class Person < GraphQL::Interface
    desc 'This is awesome!'
  end
end

Type Resolution

Interfaces have a special type_for(value, request) class-level method, which is responsible for finding an object for the given value.

The default behavior is to go over the list of types in reverse and find the first one that is a valid_member? of value.

If for some reason, that is not compatible with your application, you can override this method. For example, this relies on ActiveRecord single table inheritance column.

# app/graphql/interfaces/person.rb
def self.type_for(value, request)
  request.find_type(value.type)
end

Important It’s recommended to use request.find_type with either the symbol or string name because the resolution is cached during the request and complies with namespaces.

Such translation is possible because object names match type names when using sources. An even more accurate version would be
value.type.tr(':', '').

For gem Creators

Once you have created your interfaces in your gem, remember to add them into config.known_dependencies. It is not recommended to require such files in your gem.

Rails::GraphQL.config.known_dependencies[:interface].update(
  my_gem_interface: "#{__dir__}/interfaces/my_gem_interface",
)

Implementing Interfaces

There are two ways to implement your interfaces:

Importing Fields

By default, when an object says it implements an interface, all of the fields of the interface that the object still doesn’t have will be automatically imported as proxies. When such fields are being resolved, the interface will be instantiated and used to resolve the values, which makes them perfect for sharing resolution logic among several objects and their common fields.

# app/graphql/objects/user.rb
implements 'Person'
# OR
implements GraphQL::Person
# Then any additional fields

Proxy fields is an advanced feature. Read more about proxy fields.

Not Importing Fields

Now, if the purpose of your interface is to solely define the rules of the fields that must exist in all the objects that implement that interface, you have a couple of options:

# app/graphql/interfaces/person.rb
self.abstract = true
# Abstract interfaces will never have their fields imported
# because they should never be instantiated

# OR

# app/graphql/objects/user.rb
# All fields definition, then
implements 'Person', import_fields: false
# Not importing fields will enforce their existence and equivalency

Validation

As soon as you declare that your object implements an interface, it will validate the implementation. All fields will be checked by their equivalency. The first field with the same name that is not equivalent to the one in the interface will raise a ArgumentError exception.

Using Interfaces

You can use the interface types as the return type of your fields. However, the underlying data will always have to match one of the objects implemented by that interface. You can use a combination of the fields declared by the interface, and unique fields of each object by using spreads.

field(:recipient, 'Person')
{
  recipient {
    # Gets the name of the type
    __typename
    # Email is a common attribute
    email
    ... on User {
      # Slug is a field only available on User
      slug
    }
    ... on Admin {
      # Role is a field only available on Admin
      role
    }
  }
}

Interfaces is the best place for you to set up your Single table inheritances.