Skip to content

Commit

Permalink
feat(schema): Inference controls and improvements (gatsbyjs#13028)
Browse files Browse the repository at this point in the history
Add working inferrence controls to schema and making adding resolver and resolver options explicit.
  • Loading branch information
freiksenet authored May 16, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 8a1f99f commit 0f8febf
Showing 21 changed files with 1,032 additions and 458 deletions.
134 changes: 134 additions & 0 deletions docs/blog/2019-05-17-improvements-to-schema-customization/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
title: Improvements to Schema Customization API - Available in Gatsby 2.5.0
date: 2019-05-17
author: Mikhail Novikov
tags:
- schema
- graphql
---

Today we are releasing further improvements to the schema customization that [we've released in version 2.2.0](/blog/2019-03-18-releasing-new-schema-customization). You can use them with Gatsby 2.4.0.

It is now possible to indicate to Gatsby, that you want to add a resolver to an explicitly defined fields. Use extensions like `@link` and `@dateformat` to add default arguments or/and resolvers to fields. In addition, when `@dontInfer` is set, Gatsby will no longer run inference for marked type, allowing one to improve performance for large data sets.

## Summary

After about a month of testing schema customization both here and in pre-release we determined a couple of issues. The original aim of our schema customisation work was to remove uncertainty in people's schemas when their data changes.

However, the original design allowed some uncertainties anyway. In addition, it made inference a more heavy process, trading performance for consistency without providing a way to opt out completely. To summarize, the schema customization work released in Gatsby 2.2.0 had the following issues:

- Resolvers and arguments of fields like Date and File was determined by inferred data
- There was no easy way to use arguments/resolvers to override the above
- Inferrence was run even when `@dontInfer` flag was on
- There was no way to control inference outside of SDL, eg in Type Builders

Therefore we have some changes to the way we do inferrence. In addition, we are deprecating some of the features introduced in 2.2.0 and will remove them in Gatsby 3.

## Changes in Gatsby 2.5.0

### noDefaultResolvers and inferrence modes

First of all, we are deprecating `noDefaultResolvers`. It was an argument of `infer` and `dontInfer`. We feel it was confusing and in some cases it didn't even actually add resolvers :). We will support `noDefaultResolvers` until version 3, after which `@infer` behaviour (see below) will become a default and `noDefaultResolvers` will be removed.

We didn't want to break things, so we keep old default behaviour, even though we think it's not optimal. Add explicit `@infer` and resolver extensions (like `@link`) to fields to be future proof.

#### Default (deprecated, removed in v3)

Applies with no `@infer` and no `@dontInfer` on a type. Equals to `@infer(noDefaultResolvers: false)`.

Type gets all inferred fields added. If type has defined fields of types `Date`, `File` and any other node, and we inferred that they should have resolver options, resolver options will be added to type with a warning.

#### Strict inference (future default in v3)

Applies with `@infer` or `@infer(noDefaultResolvers: true)`.

Type gets all inferred fields added. Existing fields won't automatically get resolvers (use resolver extensions).

#### No inferrence

Applies with `@dontInfer` or `@dontInfer(noDefaultResolvers: true)`.

Inferrence won't run at all. Existing fields won't automatically get resolvers (use resolver extensions).

#### No new fields with default resolvers (deprecated, removed in v3)

Applies with `@dontInfer(noDefaultResolvers: false)`

Inferrence will run, but fields won't be added. If type has defined fields of types `Date`, `File` and any other node, and we inferred that they should have resolvers/args, resolvers/args will be added to type with a warning.

### Migrating your code

Here are suggested changes to your code if you are using schema customization already. Your code will work in Gatsby 2.5.0, but those changes will ensure it stays compatible with Gatsby 3.0

1. Add resolver directives to fields
2. Add `@infer` or `@dontInfer` to your type if you don't have it already

```graphql:title=before
type MyType {
date: Date
image: File
authorByEmail: AuthorJson
}
```

```graphql:title=after
type MyType @infer {
date: Date @dateformat
image: File @fileByRelativePath
authorByEmail: Author @link
}
```

### Resolver extensions

Add resolver and resolver options (such as arguments) to the given field. There are currently 3 extensions available.

- `@dateformat` - add date formatting arguments. Accepts `formatString` and
`locale` options that sets the defaults for this field
- `@link` - connect to a different Node. Arguments `by` and `from`, which
define which field to compare to on a remote node and which field to use on
the source node
- `@fileByRelativePath` - connect to a File node. Same arguments. The
difference from link is that this normalizes the relative path to be
relative from the path where source node is found.

```graphql
type MyType @infer {
date: Date @dateformat(formatString: "DD MMM", locale: "fi")
image: File @fileByRelativePath
authorByEmail: Author @link(by: "email")
}
```

### Type Builders and extensions

You can now apply configuration to type builder types through extension property on them.

```js
schema.createObjectType({
name: MyType,
extensions: {
infer: true,
},
fields: {
date: {
type: "Date",
extensions: {
dateformat: {
formatString: "DD MMM",
locale: "fi",
},
},
},
},
})
```

## Conclusions

With these improvements we hope we'll solve most of the issues that people are having with new schema customization. We want more feedback about this from you - please write a message to [schema customization umbrella issue](https://github.com/gatsbyjs/gatsby/issues/12272) if you encounter any problems. We are working on further improvements, like allowing users and plugins to define their own extensions (see [PR #13738](https://github.com/gatsbyjs/gatsby/pull/13738)).

Useful links:

- [createTypes Documentation](https://www.gatsbyjs.org/docs/actions/#createTypes)
- [Umbrella issue for schema customization bug reports](https://github.com/gatsbyjs/gatsby/issues/12272)
2 changes: 1 addition & 1 deletion packages/gatsby/package.json
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@
"glob": "^7.1.1",
"got": "8.0.0",
"graphql": "^14.1.1",
"graphql-compose": "6.0.3",
"graphql-compose": "^6.3.2",
"graphql-playground-middleware-express": "^1.7.10",
"hash-mod": "^0.0.5",
"invariant": "^2.2.4",
73 changes: 62 additions & 11 deletions packages/gatsby/src/redux/actions.js
Original file line number Diff line number Diff line change
@@ -1288,8 +1288,28 @@ import type GatsbyGraphQLType from "../schema/types/type-builders"
* with inferred field types, and default field resolvers for `Date` (which
* adds formatting options) and `File` (which resolves the field value as
* a `relativePath` foreign-key field) are added. This behavior can be
* customised with `@infer` and `@dontInfer` directives, and their
* `noDefaultResolvers` argument.
* customised with `@infer`, `@dontInfer` directives or extensions. Fields
* may be assigned resolver (and other option like args) with additional
* directives. Currently `@dateformat`, `@link` and `@fileByRelativePath` are
* available.
*
*
* Schema customization controls:
* * `@infer` - run inference on the type and add fields that don't exist on the
* defined type to it.
* * `@dontInfer` - don't run any inference on the type
*
* Extensions to add resolver options:
* * `@dateformat` - add date formatting arguments. Accepts `formatString` and
* `locale` options that sets the defaults for this field
* * `@link` - connect to a different Node. Arguments `by` and `from`, which
* define which field to compare to on a remote node and which field to use on
* the source node
* * `@fileByRelativePath` - connect to a File node. Same arguments. The
* difference from link is that this normalizes the relative path to be
* relative from the path where source node is found.
*
*
*
* @example
* exports.sourceNodes = ({ actions }) => {
@@ -1298,17 +1318,17 @@ import type GatsbyGraphQLType from "../schema/types/type-builders"
* """
* Markdown Node
* """
* type MarkdownRemark implements Node {
* type MarkdownRemark implements Node @infer {
* frontmatter: Frontmatter!
* }
*
* """
* Markdown Frontmatter
* """
* type Frontmatter {
* type Frontmatter @infer {
* title: String!
* author: AuthorJson!
* date: Date!
* author: AuthorJson! @link
* date: Date! @dateformat
* published: Boolean!
* tags: [String!]!
* }
@@ -1317,9 +1337,9 @@ import type GatsbyGraphQLType from "../schema/types/type-builders"
* Author information
* """
* # Does not include automatically inferred fields
* type AuthorJson implements Node @dontInfer(noFieldResolvers: true) {
* type AuthorJson implements Node @dontInfer {
* name: String!
* birthday: Date! # no default resolvers for Date formatting added
* birthday: Date! @dateformat(locale: "ru")
* }
* `
* createTypes(typeDefs)
@@ -1335,6 +1355,9 @@ import type GatsbyGraphQLType from "../schema/types/type-builders"
* frontmatter: 'Frontmatter!'
* },
* interfaces: ['Node'],
* extensions: {
* infer: true,
* },
* }),
* schema.buildObjectType({
* name: 'Frontmatter',
@@ -1345,12 +1368,40 @@ import type GatsbyGraphQLType from "../schema/types/type-builders"
* return parent.title || '(Untitled)'
* }
* },
* author: 'AuthorJson!',
* date: 'Date!',
* author: {
* type: 'AuthorJson'
* extensions: {
* link: {},
* },
* }
* date: {
* type: 'Date!'
* extensions: {
* dateformat: {},
* },
* },
* published: 'Boolean!',
* tags: '[String!]!',
* }
* })
* }),
* schema.buildObjectType({
* name: 'AuthorJson',
* fields: {
* name: 'String!'
* birthday: {
* type: 'Date!'
* extensions: {
* dateformat: {
* locale: 'ru',
* },
* },
* },
* },
* interfaces: ['Node'],
* extensions: {
* infer: false,
* },
* }),
* ]
* createTypes(typeDefs)
* }
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Kichen sink schema test passes kitchen sink query 1`] = `
exports[`Kitchen sink schema test passes kitchen sink query 1`] = `
Object {
"data": Object {
"addResolvers": Array [
"createResolvers": Array [
Object {
"code": "BdiU-TTFP4h",
"id": "1685001452849004065",
@@ -43,6 +43,7 @@ Object {
"_3invalidKey": null,
"code": "BShF_8qhtEv",
"comment": 0,
"defaultTime": "05 huhtikuu",
"id": "1486495736706552111",
"idWithDecoration": "decoration-1486495736706552111",
"image": Object {
@@ -51,6 +52,8 @@ Object {
},
},
"likes": 8,
"localeFormat": "05 huhtikuu 2017",
"localeString": "05 апреля",
"time": "05.04.2017",
},
},
@@ -59,6 +62,7 @@ Object {
"_3invalidKey": null,
"code": "BY6B8z5lR1F",
"comment": 0,
"defaultTime": "11 syyskuu",
"id": "1601601194425654597",
"idWithDecoration": "decoration-1601601194425654597",
"image": Object {
@@ -67,6 +71,8 @@ Object {
},
},
"likes": 9,
"localeFormat": "11 syyskuu 2017",
"localeString": "11 сентября",
"time": "11.09.2017",
},
},
Original file line number Diff line number Diff line change
@@ -1060,7 +1060,7 @@
"nodeAPIs": [
"createPages",
"sourceNodes",
"addResolvers"
"createResolvers"
],
"browserAPIs": [
"shouldUpdateScroll",
Loading

0 comments on commit 0f8febf

Please sign in to comment.