---
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)