---
title: Abstract types in GraphQL.js
---

# Abstract types in GraphQL.js

GraphQL includes two kinds of abstract types: interfaces and unions. These types let a single
field return values of different object types, while keeping your schema type-safe.

This guide covers how to define and resolve abstract types using GraphQL.js. It focuses on
constructing types in JavaScript using the GraphQL.js type system, not the schema definition
language (SDL).

## What are abstract types?

Most GraphQL types are concrete. They represent a specific kind of object, for example, a
`Book` or an `Author`. Abstract types let a field return different types of objects depending
on the data.

This is useful when the return type can vary but comes from a known set. For example, a `search`
field might return a book, an author, or a publisher. Abstract types let you model this kind of
flexibility while preserving validation, introspection, and tool support.

GraphQL provides two kinds of abstract types:

- Interfaces define a set of fields that multiple object types must implement.
  - Use case: A `ContentItem` interface with fields like `id`, `title`, and `publishedAt`,
    implemented by types such as `Article` and `PodcastEpisode`.
- Unions group together unrelated types that don't share any fields.
  - Use case: A `SearchResult` union that includes `Book`, `Author`, and `Publisher` types.

## Defining interfaces

To define an interface in GraphQL.js, use the `GraphQLInterfaceType` constructor. An interface
must include a `name`, definition of the shared `fields`, and should include a `resolveType`
function telling GraphQL which concrete type a given value corresponds to.

The following example defines a `ContentItem` interface for a publishing platform:

```js filename="ContentItemInterface.js"
import { GraphQLInterfaceType, GraphQLString, GraphQLNonNull } from 'graphql';

const ContentItemInterface = new GraphQLInterfaceType({
  name: 'ContentItem',
  fields: {
    id: { type: new GraphQLNonNull(GraphQLString) },
    title: { type: GraphQLString },
    publishedAt: { type: GraphQLString },
  },
  resolveType(value) {
    if (value.audioUrl) {
      return 'PodcastEpisode';
    }
    if (value.bodyText) {
      return 'Article';
    }
    return null;
  },
});

exports.ContentItemInterface = ContentItemInterface;
```

The `resolveType` function must return either the string type name corresponding
to the `GraphQLObjectType` of the given `value`, or `null` if the type could not
be determined.

## Implementing interfaces with object types

To implement an interface, define a `GraphQLObjectType` and include the interface in its
`interfaces` array. The object type must implement all fields defined by the interface.

The following example implements the `Article` and `PodcastEpisode` types that
conform to the `ContentItem` interface:

```js
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql';
import { ContentItemInterface } from './ContentItemInterface.js';

const ArticleType = new GraphQLObjectType({
  name: 'Article',
  interfaces: [ContentItemInterface],
  fields: {
    id: { type: new GraphQLNonNull(GraphQLString) },
    title: { type: GraphQLString },
    publishedAt: { type: GraphQLString },
    bodyText: { type: GraphQLString },
  },
  isTypeOf: (value) => value.bodyText !== undefined,
});

const PodcastEpisodeType = new GraphQLObjectType({
  name: 'PodcastEpisode',
  interfaces: [ContentItemInterface],
  fields: {
    id: { type: new GraphQLNonNull(GraphQLString) },
    title: { type: GraphQLString },
    publishedAt: { type: GraphQLString },
    audioUrl: { type: GraphQLString },
  },
  isTypeOf: (value) => value.audioUrl !== undefined,
});
```

The `isTypeOf` function is optional. It provides a fallback when `resolveType` isn't defined, or
when runtime values could match multiple types. If both `resolveType` and `isTypeOf` are defined,
GraphQL uses `resolveType`.

## Defining union types

Use the `GraphQLUnionType` constructor to define a union. A union allows a field to return one
of several object types that don't need to share fields.

A union requires a name and a list of object types (`types`). It should also be
provided a `resolveType` function the same as explained for interfaces above.

The following example defines a `SearchResult` union:

```js
import { GraphQLUnionType } from 'graphql';

const SearchResultType = new GraphQLUnionType({
  name: 'SearchResult',
  types: [BookType, AuthorType, PublisherType],
  resolveType(value) {
    if (value.isbn) {
      return 'Book';
    }
    if (value.bio) {
      return 'Author';
    }
    if (value.catalogSize) {
      return 'Publisher';
    }
    return null;
  },
});
```

Unlike interfaces, unions don't declare any fields their members must implement.
Clients use a fragment with a type condition to query fields from a concrete type.

## Resolving abstract types at runtime

GraphQL resolves abstract types dynamically during execution using the `resolveType` function, if
present.

This function receives the following arguments:

{/* prettier-ignore */}
```js
resolveType(value, context, info)
```

It can return:

- The name of a type as a string
- `null` if the type could not be determined
- A `Promise` resolving to either of the above

If `resolveType` isn't defined, GraphQL falls back to checking each possible type's `isTypeOf`
function. This fallback is less efficient and makes type resolution harder to debug. For most cases,
explicitly defining `resolveType` is recommended.

## Querying abstract types

To query a field that returns an abstract type, use fragments to select fields from the possible
concrete types. GraphQL evaluates each fragment based on the runtime type of the result.

For example:

```graphql
query Search($term: String! = "deep learning") {
  search(term: $term) {
    # Inline fragments with type condition:
    ... on Book {
      title
      isbn
    }
    ... on Author {
      name
      bio
    }
    # Named fragment:
    ...publisherFrag
  }
}

fragment publisherFrag on Publisher {
  name
  catalogSize
}
```

GraphQL's introspection system lists all possible types for each interface and union, which
enables code generation and editor tooling to provide type-aware completions; however you should
keep in mind the possibility that more types will implement the interface or be included in the
union in future, and thus ensure that you have a default case to handle additional types.

## Best practices

- Always implement `resolveType` for interfaces and unions to handle runtime type resolution.
- Keep `resolveType` logic simple, using consistent field shapes or tags to distinguish
  types.
- Test `resolveType` logic carefully. Errors in `resolveType` can cause runtime errors that can
  be hard to trace.
- Use interfaces when types share fields and unions when types are structurally unrelated.

## Additional resources

- [Constructing Types](./constructing-types)
- GraphQL Specification:
  - [Interfaces](https://spec.graphql.org/October2021/#sec-Interfaces)
  - [Unions](https://spec.graphql.org/October2021/#sec-Unions)