From 4b2998b2d5b17b823ccf73262f92342ec885f8ab Mon Sep 17 00:00:00 2001 From: stefanprobst Date: Thu, 23 May 2019 09:34:35 +0200 Subject: [PATCH] feat(docs): Add schema customization docs (#13832) Add schema customization docs --- docs/docs/schema-connections.md | 4 + docs/docs/schema-customization.md | 835 +++++++++++++++++++++++++++ docs/docs/schema-generation.md | 4 + docs/docs/schema-gql-type.md | 4 + docs/docs/schema-input-gql.md | 4 + docs/docs/schema-sift.md | 4 + www/src/data/sidebars/doc-links.yaml | 2 + 7 files changed, 857 insertions(+) create mode 100644 docs/docs/schema-customization.md diff --git a/docs/docs/schema-connections.md b/docs/docs/schema-connections.md index 4e750e90af31d..fdb67aa2ee3f8 100644 --- a/docs/docs/schema-connections.md +++ b/docs/docs/schema-connections.md @@ -2,6 +2,10 @@ title: Schema connections --- +> This documentation isn't up to date with latest +> [schema customization changes](/docs/schema-customization). Help Gatsby by +> making a PR to [update this documentation](https://github.com/gatsbyjs/gatsby/issues/14228)! + ## What are schema connections? So far in schema generation, we have covered how [GraphQL types are inferred](/docs/schema-gql-type), how [query arguments for types](/docs/schema-input-gql) are created, and how [sift resolvers](/docs/schema-sift) work. But all of these only allow querying down to a single node of a type. Schema connections is the ability to query over **collections** of nodes of a type. For example, if we want to query all markdown nodes by some criteria, it will allow us to write queries such as: diff --git a/docs/docs/schema-customization.md b/docs/docs/schema-customization.md new file mode 100644 index 0000000000000..900312c9589c5 --- /dev/null +++ b/docs/docs/schema-customization.md @@ -0,0 +1,835 @@ +# Customizing the GraphQL Schema + +One of Gatsby's main strengths is the ability to query data from a variety of +sources in a uniform way with GraphQL. For this to work, a GraphQL Schema must +be generated that defines the shape of the data. + +Gatsby is able to automatically infer a GraphQL Schema from your data, and in +many cases this is really all you need. There are however situations when you +either want to explicitly define the data shape, or add custom functionality to +the query layer - this is what Gatsby's Schema Customization API provides. + +The following guide walks through some examples to showcase the API. + +> This guide is aimed at plugin authors, users trying to fix GraphQL schemas +> created by automatic type inference, developers optimising builds for larger +> sites, and anyone interested in customizing Gatsby's schema generation. +> As such, the guide assumes that you're somewhat familiar with GraphQL types +> and with using Gatsby's Node APIs. + +## Explicitly defining data types + +Our example project is a blog that gets its data from local Markdown files which +provide the post contents, as well as author information in JSON format. We also +have occasional guest contributors whose info we keep in a separate JSON file. + +```markdown:title=src/data/post1.md +--- +title: Sample Post +publishedAt: 2019-04-01 +author: jane@example.com +tags: + - wow +--- + +# Heading + +Text +``` + +```json:title=src/data/author.json +[ + { + "name": "Doe", + "firstName": "Jane", + "email": "jane@example.com", + "joinedAt": "2018-01-01" + } +] +``` + +```json:title=src/data/contributor.json +[ + { + "name": "Doe", + "firstName": "Zoe", + "email": "zoe@example.com", + "receivedSwag": true + } +] +``` + +To be able to query the contents of these files with GraphQL, we need to first +load them into Gatsby's internal data store. This is what source and transformer +plugin accomplish - in this case `gatsby-source-filesystem` and +`gatsby-transformer-remark` plus `gatsby-transformer-json`. Every markdown post +file is hereby transformed into a "node" object in the internal data store with +a unique `id` and a type `MarkdownRemark`. Similarly, an author will be +represented by a node object of type `AuthorJson`, and contributor info will be +transformed into node objects of type `ContributorJson`. + +### The Node interface + +This data structure is represented in Gatsby's GraphQL schema with the `Node` +interface, which describes the set of fields common to node objects created by +source and transformer plugins (`id`, `parent`, `children`, as well as a couple +of `internal` fields like `type`). In GraphQL Schema Definition Language (SDL), +it looks like this: + +```graphql +interface Node { + id: ID! + parent: Node! + children: [Node!]! + internal: Internal! +} + +type Internal { + type: String! +} +``` + +Types created by source and transformer plugins implement this interface. For +example, the node type created by `gatsby-transformer-json` for `authors.json` +will be represented in the GraphQL schema as: + +```graphql +type AuthorJson implements Node { + id: ID! + parent: Node! + children: [Node!]! + internal: Internal! + name: String + firstName: String + email: String + joinedAt: Date +} +``` + +> A quick way to inspect the schema generated by Gatsby is the GraphQL Playground. +> Start your project with `GATSBY_GRAPHQL_IDE=playground gatsby develop`, open the +> playground at `http://localhost:8000/___graphql` and inspect the `Schema` tab on +> the right. + +### Automatic type inference + +It's important to note that the data in `author.json` does not provide type +information of the Author fields by itself. In order to translate the data +shape into GraphQL type definitions, Gatsby has to inspect the contents of +every field and check its type. In many cases this works very well and it is +still the default mechanism for creating a GraphQL schema. + +There are however two problems with this approach: (1) it is quite +time-consuming and therefore does not scale very well and (2) if the values on a +field are of different types Gatsby cannot decide which one is the correct one. +A consequence of this is that if your data sources change, type inference could +suddenly fail. + +Both problems can be solved by providing explicit type definitions for Gatsby's +GraphQL schema. + +### Creating type definitions + +Let's take the latter case first. Assume a new author joins the team, but in the +new author entry there is a typo on the `joinedAt` field: "201-04-02" which is +not a valid Date. + +```diff:title=src/data/author.json ++ { ++ "name": "Doe", ++ "firstName": "John", ++ "email": "john@example.com", ++ "joinedAt": "201-04-02" ++ } +] +``` + +This will confuse Gatsby's type inference since the `joinedAt` +field will now have both Date and String values. + +#### Fixing field types + +To ensure that the field will always be of Date type, you can provide explicit +type definitions to Gatsby with the [`createTypes`](/docs/actions/#createTypes) action. +It accepts type definitions in GraphQL Schema Definition Language: + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + type AuthorJson implements Node { + joinedAt: Date + } + ` + createTypes(typeDefs) +} +``` + +Note that the rest of the fields (`name`, `firstName` etc.) don't have to be +provided, they will still be handled by Gatsby's type inference. + +> Although the `createTypes` action is passed to all `gatsby-node` APIs, +> it has to be called before schema generation. We recommend to use the +> [`sourceNodes` API](/docs/node-apis/#sourceNodes). + +#### Opting out of type inference + +There are however advantages to providing full definitions for a node type, and +bypassing the type inference mechanism altogether. With smaller scale projects +inference is usually not a performance problem, but as projects grow the +performance penalty of having to check each field type will become noticable. + +Gatsby allows to opt out of inference with the `@dontInfer` type directive - which +in turn requires that you explicitly provide type definitions for all fields +that should be available for querying: + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + type AuthorJson implements Node @dontInfer { + name: String! + firstName: String! + email: String! + joinedAt: Date + } + ` + createTypes(typeDefs) +} +``` + +Note that you don't need to explicitly provide the Node interface fields (`id`, +`parent`, etc.), Gatsby will automatically add them for you. + +> If you wonder about the exclamation marks - those allow +> [specifying nullability](https://graphql.org/learn/schema/#lists-and-non-null) +> in GraphQL, i.e. if a field value is allowed to be `null` or not. + +#### Nested types + +So far we have only been dealing with scalar values (`String` and `Date`; +GraphQL also knows `ID`, `Int`, `Float`, `Boolean` and `JSON`). Fields can +however also contain complex object values. To target those fields in GraphQL SDL, you +can provide a full type definition for the nested type, which can be arbitrarily +named (as long as the name is unique in the schema). In our example project, the +`frontmatter` field on the `MarkdownRemark` node type is a good example. Say we +want to ensure that `frontmatter.tags` will always be an array of strings. + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + type MarkdownRemark implements Node { + frontmatter: Frontmatter + } + type Frontmatter { + tags: [String!]! + } + ` + createTypes(typeDefs) +} +``` + +Note that with `createTypes` we cannot directly target a `Frontmatter` type +without also specifying that this is the type of the `frontmatter` field on the +`MarkdownRemark` type, The following would fail because Gatsby would have no way +of knowing which field the `Frontmatter` type should be applied to: + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + # This will fail!!! + type Frontmatter { + tags: [String]! + } + ` + createTypes(typeDefs) +} +``` + +It is useful to think about your data, and the corresponding GraphQL schema, by +always starting from the Node types created by source and transformer plugins. + +> Note that the `Frontmatter` type must not implement the Node interface since +> it is not a top-level type created by source or transformer plugins: it has no +> `id` field, and is just there to describe the data shape on a nested field. + +#### Gatsby Type Builders + +In many cases, GraphQL SDL provides a succinct way to provide type definitions +for your schema. If however you need more flexibility, `createTypes` also +accepts type definitions provided with the help of Gatsby Type Builders, which +are more flexible than SDL syntax but less verbose than `graphql-js`. They are +accessible on the `schema` argument passed to Node APIs. + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions, schema }) => { + const { createTypes } = actions + const typeDefs = [ + schema.buildObjectType({ + name: "ContributorJson", + fields: { + name: "String!", + firstName: "String!", + email: "String!", + receivedSwag: { + type: "Boolean", + resolve: source => source.receivedSwag || false, + }, + }, + interfaces: ["Node"], + }), + ] + createTypes(typeDefs) +} +``` + +Gatsby Type Builders allow referencing types as simple strings, and accept full +field configs (`type`, `args`, `resolve`). When defining top-level types, don't forget +to pass `interfaces: ['Node']`, which does the same for Type Builders as adding +`implements Node` does for SDL-defined types. It is also possible to opt out of type +inference with Type Builders by setting the `infer` type extension to `false`: + +```diff +schema.buildObjectType({ + name: "ContributorJson", + fields: { + name: "String!", + }, + interfaces: ["Node"], ++ extensions: { ++ // While in SDL we have two different directives, @infer and @dontInfer to ++ // control inference behavior, Gatsby Type Builders take a single `infer` ++ // extension which accepts a Boolean ++ infer: false ++ }, +}), +``` + +> Type Builders also exist for Input, Interface and Union types: +> `buildInputType`, `buildInterfaceType`, and `buildUnionType`. +> Note that the `createTypes` action also accepts `graphql-js` types directly, +> but usually either SDL or Type Builders are the better alternatives. + +#### Foreign-key fields + +Gatsby's automatic type inference has one trick up its sleeve: for every field +that ends in `___NODE` it will interpret the field value as an `id` and create a +foreign-key relation. + +Creating foreign-key relations with the `createTypes` action, +i.e. without relying on type inference and the `___NODE` field naming +convention, requires a bit of manual setup. + +In our example project, we want the `frontmatter.author` field on +`MarkdownRemark` nodes to expand the provided field value to a full `AuthorJson` node. +For this to work, we have to provide a custom field resolver. (see below for +more info on `context.nodeModel`) + +```js:title=gatsby-node.js +exports.sourceNodes = ({ action, schema }) => { + const { createTypes } = actions + const typeDefs = [ + "type MarkdownRemark implements Node { frontmatter: Frontmatter }", + schema.buildObjectType({ + name: "Frontmatter", + fields: { + author: { + type: "AuthorJson", + resolve: (source, args, context, info) => { + // If we were linking by ID, we could use `getNodeById` to + // find the correct author: + // return context.nodeModel.getNodeById({ + // id: source.author, + // type: "AuthorJson", + // }) + // But since we are using the author email as foreign key, + // we can use `runQuery`, or simply get all author nodes + // with `getAllNodes` and manually find the linked author + // node: + return context.nodeModel + .getAllNodes({ type: "AuthorJson" }) + .find(author => author.email === source.author) + }, + }, + }, + }), + ] + createTypes(typeDefs) +} +``` + +What is happening here is that we provide a custom field resolver that asks +Gatsby's internal data store for the the full node object with the specified +`id` and `type`. + +Because creating foreign-key relations is such a common usecase, Gatsby +luckily also provides a much easier way to do this -- with the help of +extensions or directives. It looks like this: + +```graphql +type MarkdownRemark implements Node { + frontmatter: Frontmatter +} +type Frontmatter { + author: AuthorJson @link # default foreign-key relation by `id` + reviewers: [AuthorJson] @link(by: "email") # foreign-key relation by custom field +} +type AuthorJson implements Node { + posts: [MarkdownRemark] @link(by: "frontmatter.author", from: "email") # easy back-ref +} +``` + +You simply provide a `@link` directive on a field and Gatbsy will internally +add a resolver that is quite similar to the one we wrote manually above. If no +argument is provided, Gatsby will use the `id` field as the foreign-key, +otherwise the foreign-key has to be provided with the `by` argument. The +optional `from` argument allows getting the foreign-keys from the specified +field, which is especially helpful when adding a field for back-linking. + +> Note that when using `createTypes` to fix type inference for a foreign-key field +> created by a plugin, the underlying data will probably live on a field with +> a `___NODE` suffix. Use the `from` argument to point the `link` extension to +> the correct field name. For example: `author: [AuthorJson] @link(from: "author___NODE")`. + +#### Extensions and directives + +Out of the box, Gatsby provides [four extensions](/docs/actions/#createTypes) +that allow adding custom functionality to fields without having to manually +write field resolvers: the `link` extension has already been discussed above, +`dateformat` allows adding date formatting options, `fileByRelativePath` is +similar to `link` but will resolve relative paths when linking to `File` nodes, +and `proxy` is helpful when dealing with data that contains field names with +characteres that are invalid in GraphQL. + +To add an extension to a field you can either use a directive in SDL, or the +`extensions` property when using Gatsby Type Builders: + +```js:title=gatsby-node.js +exports.sourceNodes = ({ action, schema }) => { + const { createTypes } = actions + const typeDefs = [ + "type MarkdownRemark implements Node { frontmatter: Frontmatter }", + `type Frontmatter { + publishedAt: Date @dateformat(formatString: "DD-MM-YYYY") + }` + schema.buildObjectType({ + name: 'AuthorJson', + fields: { + joinedAt: { + type: 'Date', + extensions: { + dateformat: {} + } + } + } + }) + ] + createTypes(typeDefs) +} +``` + +The above example adds [date formatting options](/docs/graphql-reference/#dates) +to the `AuthorJson.joinedAt` and the `MarkdownRemark.frontmatter.publishedAt` +fields. Those options are available as field arguments when querying those fields: + +```graphql +query { + allAuthorJson { + joinedAt(fromNow: true) + } +} +``` + +For `publishedAt` we also provide a default `formatString` which will be used +when no explicit formatting options are provided in the query. + +#### Setting default field values + +For setting default field values, Gatsby currently does not (yet) provide an +out-of-the-box extension, so resolving a field to a default value (instead of +`null`) requires manually adding a field resolver. For example, to add a default +tag to every blog post: + +```js:title=gatsby-node.js +exports.sourceNodes = ({ action, schema }) => { + const { createTypes } = actions + const typeDefs = [ + "type MarkdownRemark implements Node { frontmatter: Frontmatter }", + schema.buildObjectType({ + name: "Frontmatter", + fields: { + tags: { + type: "[String!]", + resolve(source, args, context, info) { + // For a more generic solution, we could pick the field value from + // `source[info.fieldName]` + const { tags } = source + if (source.tags == null || (Array.isArray(tags) && !tags.length)) { + return ["uncategorized"] + } + return tags + }, + }, + }, + }), + ] + createTypes(typeDefs) +} +``` + +## createResolvers API + +While it is possible to directly pass `args` and `resolvers` along the type +definitions using Gatsby Type Builders, an alternative approach specifically +tailored towards adding custom resolvers to fields is the `createResolvers` Node +API. + +```js:title=gatsby-node.js +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + Frontmatter: { + author: { + resolve(source, args, context, info) { + return context.nodeModel.getNodeById({ + id: source.author, + type: "AuthorJson", + }) + }, + }, + }, + } + createResolvers(resolvers) +} +``` + +Note that `createResolvers` allows adding new fields to types, modifying `args` +and `resolver` -- but not overriding the field type. This is because +`createResolvers` is run last in schema generation, and modifying a field type +would mean having to regenerate corresponding input types (`filter`, `sort`), +which we want to avoid. If possible, specifying field types should be done with +the `createTypes` action. + +### Accessing Gatsby's data store from field resolvers + +As mentioned above, Gatsby's internal data store and query capabilities are +available to custom field resolvers on the `context.nodeModel` argument passed +to every resolver. Accessing node(s) by `id` (and optional `type`) is possible +with [`getNodeById`](/docs/node-model/#getNodeById) and +[`getNodesByIds`](/docs/node-model/#getNodesByIds). To get all nodes, or all +nodes of a certain type, use [`getAllNodes`](/docs/node-model/#getAllNodes). +And running a query from inside your resolver functions can be accomplished +with [`runQuery`](/docs/node-model/#runQuery), which accepts `filter`, `sort`, +`limit` and `skip` query arguments. + +We could for example add a field to the `AuthorJson` type that lists all recent +posts by an author: + +```js:title=gatsby-node.js +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + AuthorJson: { + recentPosts: { + type: ["MarkdownRemark"], + resolve(source, args, context, info) { + return context.nodeModel.runQuery({ + query: { + filter: { + frontmatter: { + author: { eq: source.email }, + date: { gt: "2019-01-01" }, + }, + }, + }, + type: "MarkdownRemark", + firstOnly: false, + }) + }, + }, + }, + } + createResolvers(resolvers) +} +``` + +### Custom query fields + +One powerful approach enabled by `createResolvers` is adding custom root query +fields. While the default root query fields added by Gatsby (e.g. +`markdownRemark` and `allMarkdownRemark`) provide the whole range of query +options, query fields designed specifically for your project can be useful. For +example, we can add a query field for all external contributors to our example blog +who have received their swag: + +```js:title=gatsby-node.js +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + Query: { + contributorsWithSwag: { + type: ["ContributorJson"], + resolve(source, args, context, info) { + return context.nodeModel.runQuery({ + query: { + filter: { + receivedSwag: { eq: true }, + }, + }, + type: "ContributorJson", + firstOnly: false, + }) + }, + }, + }, + } + createResolvers(resolvers) +} +``` + +Because we might also be interested in the reverse - which contributors haven't +received their swag yet - why not add a (required) custom query arg? + +```js:title=gatsby-node.js +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + Query: { + contributors: { + type: ["ContributorJson"], + args: { + receivedSwag: "Boolean!", + }, + resolve(source, args, context, info) { + return context.nodeModel.runQuery({ + query: { + filter: { + receivedSwag: { eq: args.receivedSwag }, + }, + }, + type: "ContributorJson", + firstOnly: false, + }) + }, + }, + }, + } + createResolvers(resolvers) +} +``` + +It is also possible to provide more complex custom input types which can be defined +directly inline in SDL. We could for example add a field to the `ContributorJson` +type that counts the number of posts by a contributor, and then add a custom root +query field `contributors` which accepts `min` or `max` arguments to only return +contributors who have written at least `min`, or at most `max` number of posts: + +```js:title=gatsby-node.js +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + Query: { + contributors: { + type: ["ContributorJson"], + args: { + postsCount: "input PostsCountInput { min: Int, max: Int }", + }, + resolve(source, args, context, info) { + const { max, min = 0 } = args.postsCount || {} + const operator = max != null ? { lte: max } : { gte: min } + return context.nodeModel.runQuery({ + query: { + filter: { + posts: operator, + }, + }, + type: "ContributorJson", + firstOnly: false, + }) + }, + }, + }, + ContributorJson: { + posts: { + type: `Int`, + resolve: (source, args, context, info) => { + return context.nodeModel + .getAllNodes({ type: "MarkdownRemark" }) + .filter(post => post.frontmatter.author === source.email).length + }, + }, + }, + } + createResolvers(resolvers) +} +``` + +### Taking care of hot reloading + +When creating custom field resolvers, it is important to ensure that Gatsby +knows about the data a page depends on for hot reloading to work properly. When +you retrieve nodes from the store with [`context.nodeModel`](/docs/node-model/) methods, +it is usually not necessary to do anything manually, because Gatsby will register +dependencies for the query results automatically. The exception is `getAllNodes` +which will _not_ register data dependencies by default. This is because +requesting re-running of queries when any node of a certain type changes is +potentially a very expensive operation. If you are sure you really need this, +you can add a page data dependency either programmatically with +[`context.nodeModel.trackPageDependencies`](/docs/node-model/#trackPageDependencies), or with: + +```js +context.nodeModel.getAllNodes( + { type: "MarkdownRemark" }, + { connectionType: "MarkdownRemark" } +) +``` + +## Custom Interfaces and Unions + +Finally, let's say we want to have a page on our example blog that lists all +team members (authors and contributors). What we could do is have two queries, +one for `allAuthorJson` and one for `allContributorJson` and manually merge +those. GraphQL however provides a more elegant solution to these kinds of +problems with "abstract types" (Interfaces and Unions). Since authors and +contributors actually share most of the fields, we can abstract those up into +a `TeamMember` interface and add a custom query field for all team members +(as well as a custom resolver for full names): + +```js:title=gatsby-node.js +exports.sourceNodes = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + interface TeamMember { + name: String! + firstName: String! + email: String! + } + + type AuthorJson implements Node & TeamMember { + name: String! + firstName: String! + email: String! + joinedAt: Date + } + + type ContributorJson implements Node & TeamMember { + name: String! + firstName: String! + email: String! + receivedSwag: Boolean + } + ` +} + +exports.createResolvers = ({ createResolvers }) => { + const fullName = { + type: "String", + resolve(source, args, context, info) { + return source.firstName + " " + source.name + }, + } + const resolvers = { + Query: { + allTeamMembers: { + type: ["TeamMember"], + resolve(source, args, context, info) { + return context.nodeModel.getAllNodes({ type: "TeamMember" }) + }, + }, + }, + AuthorJson: { + fullName, + }, + ContributorJson: { + fullName, + }, + } + createResolvers(resolvers) +} +``` + +To use the newly added root query field in a page query to get the full names of +all team members, we can write: + +```js +export const query = graphql` + { + allTeamMembers { + ... on Author { + fullName + } + ... on Contributor { + fullName + } + } + } +` +``` + +## Extending third-party types + +So far, the examples have been dealing with types created from locally available data. +However, Gatsby also allows to integrate and modify third-party GraphQL schemas. + +Usually, those third-party schemas are retrieved from remote sources via introspection +query with Gatsby's `gatsby-source-graphql` plugin. To customize types integrated from +a third-party schema, we can use the [`createResolvers`](/docs/node-apis/#createResolvers) API. + +### Feeding remote images into `gatsby-image` + +As an example, let's look at [using-gatsby-source-graphql](https://github.com/gatsbyjs/gatsby/blob/master/examples/using-gatsby-source-graphql/gatsby-node.js) to see how we could use `createResolvers` to feed images from a CMS into `gatsby-image` (the assumption is that `gatsby-source-graphql` was configured +to prefix all types from the third-party schema with `CMS`): + +```js:title=gatsby-node.js +exports.createResolvers = ({ + actions, + cache, + createNodeId, + createResolvers, + store, +}) => { + const { createNode } = actions + createResolvers({ + CMS_Asset: { + imageFile: { + type: `File`, + resolve(source, args, context, info) { + return createRemoteFileNode({ + url: source.url, + store, + cache, + createNode, + createNodeId, + }) + }, + }, + }, + }) +} +``` + +We create a new `imageFile` field on the `CMS_Asset` type, which will create `File` +nodes from every value on the `url` field. Since `File` nodes automatically have +`childImageSharp` convenience fields available, we can feed the images from the CMS +into `gatsby-image` by simply querying: + +```graphql +query { + cms { + post { + # In this example, the `post.image` field is of type `CMS_Asset` + image { + # It is important to include all fields in the query which we want to + # access in the resolver. In this example we want to make sure to include + # the `url` field. In the future, Gatsby might provide a `@projection` + # extension to automatically include those fields. + url + imageFile { + childImageSharp { + fixed { + ...GatsbyImageSharpFixed + } + } + } + } + } + } +} +``` diff --git a/docs/docs/schema-generation.md b/docs/docs/schema-generation.md index 596b8c969ffd9..8646de30b5d14 100644 --- a/docs/docs/schema-generation.md +++ b/docs/docs/schema-generation.md @@ -2,6 +2,10 @@ title: Schema Generation --- +> This documentation isn't up to date with latest +> [schema customization changes](/docs/schema-customization). Help Gatsby by +> making a PR to [update this documentation](https://github.com/gatsbyjs/gatsby/issues/14228)! + Once the nodes have been sourced and transformed, the next step is to generate the GraphQL Schema. This is one of the more complex parts of the Gatsby code base. In fact, as of writing, it accounts for a third of the lines of code in core Gatsby. It involves inferring a GraphQL schema from all the nodes that have been sourced and transformed so far. Read on to find out how it's done. ### Group all nodes by type diff --git a/docs/docs/schema-gql-type.md b/docs/docs/schema-gql-type.md index 3d2a1a63a29f4..3d39892aee9b8 100644 --- a/docs/docs/schema-gql-type.md +++ b/docs/docs/schema-gql-type.md @@ -2,6 +2,10 @@ title: GraphQL Node Types Creation --- +> This documentation isn't up to date with latest +> [schema customization changes](/docs/schema-customization). Help Gatsby by +> making a PR to [update this documentation](https://github.com/gatsbyjs/gatsby/issues/14228)! + Gatsby creates a [GraphQLObjectType](https://graphql.org/graphql-js/type/#graphqlobjecttype) for each distinct `node.internal.type` that is created during the source-nodes phase. Find out below how this is done. ## GraphQL Types for each type of node diff --git a/docs/docs/schema-input-gql.md b/docs/docs/schema-input-gql.md index 71bcb2e7a5104..5b764af480a43 100644 --- a/docs/docs/schema-input-gql.md +++ b/docs/docs/schema-input-gql.md @@ -2,6 +2,10 @@ title: Inferring Input Filters --- +> This documentation isn't up to date with latest +> [schema customization changes](/docs/schema-customization). Help Gatsby by +> making a PR to [update this documentation](https://github.com/gatsbyjs/gatsby/issues/14228)! + ## Input Filters vs gqlType In [gqlTypes](/docs/schema-gql-type), we inferred a Gatsby Node's main fields. These allow us to query a node's children, parent and object fields. But these are only useful once a top level GraphQL Query has returned results. In order to query by those fields, we must create GraphQL objects for input filters. E.g, querying for all markdownRemark nodes that have 4 paragraphs. diff --git a/docs/docs/schema-sift.md b/docs/docs/schema-sift.md index 20d1119b06e81..c8eaa72d109fd 100644 --- a/docs/docs/schema-sift.md +++ b/docs/docs/schema-sift.md @@ -2,6 +2,10 @@ title: Querying with Sift --- +> This documentation isn't up to date with latest +> [schema customization changes](/docs/schema-customization). Help Gatsby by +> making a PR to [update this documentation](https://github.com/gatsbyjs/gatsby/issues/14228)! + ## Summary Gatsby stores all data loaded during the source-nodes phase in Redux. And it allows you to write GraphQL queries to query that data. But Redux is a plain JavaScript object store. So how does Gatsby query over those nodes using the GraphQL query language? diff --git a/www/src/data/sidebars/doc-links.yaml b/www/src/data/sidebars/doc-links.yaml index 92a8a927730cc..468173468359d 100644 --- a/www/src/data/sidebars/doc-links.yaml +++ b/www/src/data/sidebars/doc-links.yaml @@ -146,6 +146,8 @@ link: /docs/adding-markdown-pages/ - title: Adding a List of Markdown Blog Posts link: /docs/adding-a-list-of-markdown-blog-posts/ + - title: Customizing the GraphQL Schema + link: /docs/schema-customization/ - title: Plugins link: /docs/plugins/ items: