Skip to content

Commit b83f31c

Browse files
sarahxsandersyaacovCR
authored andcommitted
docs: add page on abstract types (#4393)
1 parent dbb724d commit b83f31c

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed

website/pages/docs/_meta.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const meta = {
1717
title: 'Advanced Guides',
1818
},
1919
'constructing-types': '',
20+
'abstract-types': '',
2021
'oneof-input-objects': '',
2122
'defer-stream': '',
2223
'cursor-based-pagination': '',

website/pages/docs/abstract-types.mdx

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
---
2+
title: Abstract types in GraphQL.js
3+
---
4+
5+
GraphQL includes two kinds of abstract types: interfaces and unions. These types let a single
6+
field return values of different object types, while keeping your schema type-safe.
7+
8+
This guide covers how to define and resolve abstract types using GraphQL.js. It focuses on
9+
constructing types in JavaScript using the GraphQL.js type system, not the schema definition
10+
language (SDL).
11+
12+
## What are abstract types?
13+
14+
Most GraphQL types are concrete. They represent a specific kind of object, for example, a
15+
`Book` or an `Author`. Abstract types let a field return different types of objects depending
16+
on the data.
17+
18+
This is useful when the return type can vary but comes from a known set. For example, a `search`
19+
field might return a book, an author, or a publisher. Abstract types let you model this kind of
20+
flexibility while preserving validation, introspection, and tool support.
21+
22+
GraphQL provides two kinds of abstract types:
23+
24+
- Interfaces define a set of fields that multiple object types must implement.
25+
- Use case: A `ContentItem` interface with fields like `id`, `title`, and `publishedAt`,
26+
implemented by types such as `Article` and `PodcastEpisode`.
27+
- Unions group together unrelated types that don't share any fields.
28+
- Use case: A `SearchResult` union that includes `Book`, `Author`, and `Publisher` types.
29+
30+
## Defining interfaces
31+
32+
To define an interface in GraphQL.js, use the `GraphQLInterfaceType` constructor. An interface
33+
must include a `name`, a `fields` function, and a `resolveType` function, which tells GraphQL which
34+
concrete type a given value corresponds to.
35+
36+
The following example defines a `ContentItem` interface for a publishing platform:
37+
38+
```js
39+
const { GraphQLInterfaceType, GraphQLString, GraphQLNonNull } = require('graphql');
40+
41+
const ContentItemInterface = new GraphQLInterfaceType({
42+
name: 'ContentItem',
43+
fields: {
44+
id: { type: new GraphQLNonNull(GraphQLString) },
45+
title: { type: GraphQLString },
46+
publishedAt: { type: GraphQLString },
47+
},
48+
resolveType(value) {
49+
if (value.audioUrl) {
50+
return 'PodcastEpisode';
51+
}
52+
if (value.bodyText) {
53+
return 'Article';
54+
}
55+
return null;
56+
},
57+
});
58+
```
59+
60+
You can return either the type name as a string or the corresponding `GraphQLObjectType` instance.
61+
Returning the instance is recommended when possible for better type safety and tooling support.
62+
63+
## Implementing interfaces with object types
64+
65+
To implement an interface, define a `GraphQLObjectType` and include the interface in its
66+
`interfaces` array. The object type must implement all fields defined by the interface.
67+
68+
The following example implements the `Article` and `PodcastEpisode` types that
69+
conform to the `ContentItem` interface:
70+
71+
```js
72+
const { GraphQLObjectType, GraphQLString, GraphQLNonNull } = require('graphql');
73+
74+
const ArticleType = new GraphQLObjectType({
75+
name: 'Article',
76+
interfaces: [ContentItemInterface],
77+
fields: {
78+
id: { type: new GraphQLNonNull(GraphQLString) },
79+
title: { type: GraphQLString },
80+
publishedAt: { type: GraphQLString },
81+
bodyText: { type: GraphQLString },
82+
},
83+
isTypeOf: (value) => value.bodyText !== undefined,
84+
});
85+
86+
const PodcastEpisodeType = new GraphQLObjectType({
87+
name: 'PodcastEpisode',
88+
interfaces: [ContentItemInterface],
89+
fields: {
90+
id: { type: new GraphQLNonNull(GraphQLString) },
91+
title: { type: GraphQLString },
92+
publishedAt: { type: GraphQLString },
93+
audioUrl: { type: GraphQLString },
94+
},
95+
isTypeOf: (value) => value.audioUrl !== undefined,
96+
});
97+
```
98+
99+
The `isTypeOf` function is optional. It provides a fallback when `resolveType` isn't defined, or
100+
when runtime values could match multiple types. If both `resolveType` and `isTypeOf` are defined,
101+
GraphQL uses `resolveType`.
102+
103+
## Defining union types
104+
105+
Use the `GraphQLUnionType` constructor to define a union. A union allows a field to return one
106+
of several object types that don't need to share fields.
107+
108+
A union requires:
109+
110+
- A `name`
111+
- A list of object types (`types`)
112+
- A `resolveType` function
113+
114+
The following example defines a `SearchResult` union:
115+
116+
```js
117+
const { GraphQLUnionType } = require('graphql');
118+
119+
const SearchResultType = new GraphQLUnionType({
120+
name: 'SearchResult',
121+
types: [BookType, AuthorType, PublisherType],
122+
resolveType(value) {
123+
if (value.isbn) {
124+
return 'Book';
125+
}
126+
if (value.bio) {
127+
return 'Author';
128+
}
129+
if (value.catalogSize) {
130+
return 'Publisher';
131+
}
132+
return null;
133+
},
134+
});
135+
```
136+
137+
Unlike interfaces, unions don’t declare any fields of their own. Clients use inline fragments
138+
to query fields from the concrete types.
139+
140+
## Resolving abstract types at runtime
141+
142+
GraphQL resolves abstract types dynamically during execution using the `resolveType` function.
143+
144+
This function receives the following arguments:
145+
146+
```js
147+
resolveType(value, context, info)
148+
```
149+
150+
It can return:
151+
152+
- A `GraphQLObjectType` instance (recommended)
153+
- The name of a type as a string
154+
- A `Promise` resolving to either of the above
155+
156+
If `resolveType` isn't defined, GraphQL falls back to checking each possible type's `isTypeOf`
157+
function. This fallback is less efficient and makes type resolution harder to debug. For most cases,
158+
explicitly defining `resolveType` is recommended.
159+
160+
## Querying abstract types
161+
162+
To query a field that returns an abstract type, use inline fragments to select fields from the
163+
possible concrete types. GraphQL evaluates each fragment based on the runtime type of the result.
164+
165+
For example:
166+
167+
```graphql
168+
{
169+
search(term: "deep learning") {
170+
... on Book {
171+
title
172+
isbn
173+
}
174+
... on Author {
175+
name
176+
bio
177+
}
178+
... on Publisher {
179+
name
180+
catalogSize
181+
}
182+
}
183+
}
184+
```
185+
186+
GraphQL's introspection system lists all possible types for each interface and union, which
187+
enables code generation and editor tooling to provide type-aware completions.
188+
189+
## Best practices
190+
191+
- Always implement `resolveType` for interfaces and unions to handle runtime type resolution.
192+
- Return the `GraphQLObjectType` instance when possible for better clarity and static analysis.
193+
- Keep `resolveType` logic simple, using consistent field shapes or tags to distinguish
194+
types.
195+
- Test `resolveType` logic carefully. Errors in `resolveType` can cause runtime errors that can
196+
be hard to trace.
197+
- Use interfaces when types share fields and unions when types are structurally unrelated.
198+
199+
## Additional resources
200+
201+
- [Constructing Types](https://www.graphql-js.org/docs/constructing-types/)
202+
- GraphQL Specification:
203+
- [Interfaces](https://spec.graphql.org/October2021/#sec-Interfaces)
204+
- [Unions](https://spec.graphql.org/October2021/#sec-Unions)

0 commit comments

Comments
 (0)