Skip to content

Commit 6365768

Browse files
authored
feat(gatsby): support GraphQL interface inheritance (#29427)
* feat(gatsby): support interface inheritance * Move deprecation message to directive description deprecationReason is not supported for directives :/ * Add deprecation warning for interfaces with @nodeInterface extension * update docs * minor tweak * add tests for non-node interface inheritance * update warning text * update error text
1 parent ea3facd commit 6365768

File tree

11 files changed

+725
-58
lines changed

11 files changed

+725
-58
lines changed

docs/docs/reference/graphql-data-layer/schema-customization.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,18 +1000,17 @@ export const query = graphql`
10001000
`
10011001
```
10021002

1003-
### Queryable interfaces with the `@nodeInterface` extension
1003+
### Queryable interfaces
10041004

1005-
Since Gatsby 2.13.22, you can achieve the same thing as above by adding the `@nodeInterface`
1006-
extension to the `TeamMember` interface. This will treat the interface like a normal
1007-
top-level type that implements the `Node` interface, and thus automatically add root
1008-
query fields for the interface.
1005+
Since Gatsby 3.0.0, you can use interface inheritance to achieve the same thing as above:
1006+
`TeamMember implements Node`. This will treat the interface like a normal top-level type that
1007+
implements the `Node` interface, and thus automatically add root query fields for the interface.
10091008

10101009
```js:title=gatsby-node.js
10111010
exports.createSchemaCustomization = ({ actions }) => {
10121011
const { createTypes } = actions
10131012
const typeDefs = `
1014-
interface TeamMember @nodeInterface {
1013+
interface TeamMember implements Node {
10151014
id: ID!
10161015
name: String!
10171016
firstName: String!
@@ -1079,8 +1078,7 @@ data.allTeamMember.nodes.map(node => {
10791078
})
10801079
```
10811080

1082-
> Note: All types implementing an interface with the `@nodeInterface` extension
1083-
> must also implement the `Node` interface.
1081+
> Note: All types implementing a queryable interface must also implement the `Node` interface
10841082
10851083
## Extending third-party types
10861084

packages/gatsby/src/schema/__tests__/__snapshots__/build-schema.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ directive @childOf(
4141
) on OBJECT
4242
4343
\\"\\"\\"
44+
DEPRECATED: Use interface inheritance instead, i.e. \\"interface Foo implements Node\\".
45+
4446
Adds root query fields for an interface. All implementing types must also implement the Node interface.
4547
\\"\\"\\"
4648
directive @nodeInterface on INTERFACE

packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ type OneMoreTest implements Node @dontInfer {
276276
bar: Boolean
277277
}
278278
279+
interface ITest2 implements Node {
280+
id: ID!
281+
date: Date @dateformat(formatString: \\"YYYY\\")
282+
}
283+
279284
union ThisOrThat = AnotherTest | OneMoreTest
280285
281286
type Inline {
@@ -436,6 +441,11 @@ type OneMoreTest implements Node @dontInfer {
436441
bar: Boolean
437442
}
438443
444+
interface ITest2 implements Node {
445+
id: ID!
446+
date: Date @dateformat(formatString: \\"YYYY\\")
447+
}
448+
439449
union ThisOrThat = AnotherTest | OneMoreTest
440450
441451
type Inline {

packages/gatsby/src/schema/__tests__/__snapshots__/rebuild-schema.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ directive @childOf(
4141
) on OBJECT
4242
4343
\\"\\"\\"
44+
DEPRECATED: Use interface inheritance instead, i.e. \\"interface Foo implements Node\\".
45+
4446
Adds root query fields for an interface. All implementing types must also implement the Node interface.
4547
\\"\\"\\"
4648
directive @nodeInterface on INTERFACE

packages/gatsby/src/schema/__tests__/build-schema.js

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,23 @@ describe(`Build schema`, () => {
211211

212212
it(`allows adding abstract types in SDL`, async () => {
213213
createTypes(`
214-
interface FooBar {
214+
interface Text {
215+
text: String
216+
}
217+
218+
interface FooBar implements Text {
215219
text: String!
216220
}
217221
218-
type Foo implements Node & FooBar {
222+
type Foo implements Node & Text & FooBar {
219223
text: String!
220224
}
221225
222-
type Bar implements Node & FooBar {
226+
type Bar implements Node & Text & FooBar {
223227
text: String!
224228
}
225229
226-
type Author implements Node & FooBar {
230+
type Author implements Node & Text & FooBar {
227231
text: String!
228232
}
229233
@@ -232,49 +236,64 @@ describe(`Build schema`, () => {
232236

233237
const schema = await buildSchema()
234238

235-
const interfaceType = schema.getType(`FooBar`)
236-
expect(interfaceType).toBeInstanceOf(GraphQLInterfaceType)
239+
const textType = schema.getType(`Text`)
240+
expect(textType).toBeInstanceOf(GraphQLInterfaceType)
241+
const fooBarType = schema.getType(`FooBar`)
242+
expect(fooBarType).toBeInstanceOf(GraphQLInterfaceType)
243+
expect(fooBarType.getInterfaces()).toEqual([schema.getType(`Text`)])
237244
const unionType = schema.getType(`UFooBar`)
238245
expect(unionType).toBeInstanceOf(GraphQLUnionType)
239246
;[(`Foo`, `Bar`, `Author`)].forEach(typeName => {
240247
const type = schema.getType(typeName)
241248
const typeSample = { internal: { type: typeName } }
242-
expect(interfaceType.resolveType(typeSample)).toBe(typeName)
249+
expect(textType.resolveType(typeSample)).toBe(typeName)
250+
expect(fooBarType.resolveType(typeSample)).toBe(typeName)
243251
expect(unionType.resolveType(typeSample)).toBe(typeName)
244252
expect(new Set(type.getInterfaces())).toEqual(
245-
new Set([schema.getType(`Node`), schema.getType(`FooBar`)])
253+
new Set([
254+
schema.getType(`Node`),
255+
schema.getType(`FooBar`),
256+
schema.getType(`Text`),
257+
])
246258
)
247259
})
248260
})
249261

250262
it(`allows adding abstract types in gatsby type def language`, async () => {
251263
createTypes([
264+
buildInterfaceType({
265+
name: `Text`,
266+
fields: {
267+
text: `String!`,
268+
},
269+
}),
252270
buildInterfaceType({
253271
name: `FooBar`,
254272
fields: {
255273
text: `String!`,
256274
},
275+
interfaces: [`Text`],
257276
}),
258277
buildObjectType({
259278
name: `Foo`,
260279
fields: {
261280
text: `String!`,
262281
},
263-
interfaces: [`Node`, `FooBar`],
282+
interfaces: [`Node`, `Text`, `FooBar`],
264283
}),
265284
buildObjectType({
266285
name: `Bar`,
267286
fields: {
268287
text: `String!`,
269288
},
270-
interfaces: [`Node`, `FooBar`],
289+
interfaces: [`Node`, `Text`, `FooBar`],
271290
}),
272291
buildObjectType({
273292
name: `Author`,
274293
fields: {
275294
text: `String!`,
276295
},
277-
interfaces: [`Node`, `FooBar`],
296+
interfaces: [`Node`, `Text`, `FooBar`],
278297
}),
279298
buildUnionType({
280299
name: `UFooBar`,
@@ -284,18 +303,26 @@ describe(`Build schema`, () => {
284303

285304
const schema = await buildSchema()
286305

287-
const interfaceType = schema.getType(`FooBar`)
288-
expect(interfaceType).toBeInstanceOf(GraphQLInterfaceType)
289-
const unionType = schema.getType(`UFooBar`)
290-
expect(unionType).toBeInstanceOf(GraphQLUnionType)
291-
expect(unionType.getTypes().length).toBe(3)
306+
const textType = schema.getType(`Text`)
307+
expect(textType).toBeInstanceOf(GraphQLInterfaceType)
308+
const fooBarType = schema.getType(`FooBar`)
309+
expect(fooBarType).toBeInstanceOf(GraphQLInterfaceType)
310+
expect(fooBarType.getInterfaces()).toEqual([textType])
311+
const unionFooBarType = schema.getType(`UFooBar`)
312+
expect(unionFooBarType).toBeInstanceOf(GraphQLUnionType)
313+
expect(unionFooBarType.getTypes().length).toBe(3)
292314
;[(`Foo`, `Bar`, `Author`)].forEach(typeName => {
293315
const type = schema.getType(typeName)
294316
const typeSample = { internal: { type: typeName } }
295-
expect(interfaceType.resolveType(typeSample)).toBe(typeName)
296-
expect(unionType.resolveType(typeSample)).toBe(typeName)
317+
expect(textType.resolveType(typeSample)).toBe(typeName)
318+
expect(fooBarType.resolveType(typeSample)).toBe(typeName)
319+
expect(unionFooBarType.resolveType(typeSample)).toBe(typeName)
297320
expect(new Set(type.getInterfaces())).toEqual(
298-
new Set([schema.getType(`Node`), schema.getType(`FooBar`)])
321+
new Set([
322+
schema.getType(`Node`),
323+
schema.getType(`FooBar`),
324+
schema.getType(`Text`),
325+
])
299326
)
300327
})
301328
})

packages/gatsby/src/schema/__tests__/print.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ describe(`Print type definitions`, () => {
9393
id: ID!
9494
date: Date @dateformat(formatString: "YYYY")
9595
}
96+
interface ITest2 implements Node {
97+
id: ID!
98+
date: Date @dateformat(formatString: "YYYY")
99+
}
96100
union ThisOrThat = AnotherTest | OneMoreTest
97101
`)
98102
typeDefs.push(

packages/gatsby/src/schema/extensions/__tests__/child-relations.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
830830
await buildSchema()
831831
expect(report.error).toBeCalledWith(
832832
`With the \`childOf\` extension, children fields can only be added to ` +
833-
`interfaces which have the \`@nodeInterface\` extension.\n` +
833+
`interfaces which implement the \`Node\` interface.\n` +
834834
`Check the type definition of \`Ancestors\`.`
835835
)
836836
})

0 commit comments

Comments
 (0)