如何在多种类型上使用GraphQL片段

Cas*_*mir 7 dry contentful graphql gatsby

我有一个Gatsby项目,它有两个不同类型的内容非常相似的GraphQL查询:常规页面和维基文章.

页面由slug

export const query = graphql`
  query($slug: String!) {
    page: contentfulPage(slug: {eq: $slug}) {
      title
      slug
      body {
        remark: childMarkdownRemark {
          excerpt
          html
          headings {
            value
            depth
          }
        }
      }
      updatedAt(formatString: "D. MMM YYYY")
      authors {
        name
        email
      }
    }
  }
`
Run Code Online (Sandbox Code Playgroud)

由slug撰写的Wiki文章

export const query = graphql`
  query($slug: String!) {
    article: contentfulWikiArticle(slug: {eq: $slug}) {
      title
      slug
      body {
        remark: childMarkdownRemark {
          excerpt
          html
          headings {
            value
            depth
          }
        }
      }
      updatedAt(formatString: "D. MMM YYYY")
      authors {
        name
        email
      }
 +    section {
 +      title
 +      slug
 +    }
 +    subsection {
 +      title
 +      slug
 +    }
    }
  }
`
Run Code Online (Sandbox Code Playgroud)

除了维基文章的附加部分和子部分,查询是相同的.为了保持DRY,我怎样才能将页面字段移动到一个单独的片段中,尽管它的类型不同,也可以传播到wiki文章查询中?GraphQL可以提供如下内容:

fragment pageFields on [ContenfulPage, ContenfulWikiArticle] {
  ...
}
Run Code Online (Sandbox Code Playgroud)

Der*_*yen 8

Gatsby 最近发布的版本允许用户为 graphql 模式设置自己的类型,使这个问题最终成为可能。

如果用户可以控制架构,graphql 总是可能的,但是由于最近的 Gatsby 更新,用户最终可以自己实现这一点。

设置

为了建立一个简单的例子,我将gatsby-transformer-json在一个像这样的简单文件夹上使用

jsonFolder
  |--one.json { "type": "One", "name": "a", "food": "pizza" }
  `--two.json { "type": "Two", "name": "b", "game": "chess" }
Run Code Online (Sandbox Code Playgroud)

并使用选项声明我的类型名称:

{
  resolve: `gatsby-transformer-json`,
  options: { 
    typeName: ({ object }) => object.type,
  },
},
Run Code Online (Sandbox Code Playgroud)

现在我有两种类型是为我创建的。我可以在其中一个上创建一个片段,但不能同时创建:

export const name = graphql`
  fragment name on One {
    name
  }
`

export const pageQuery = graphql`
  query {
    one {
      ...name
    }
    two {
      ...name <-- ?? throw type error
    }
  }
`
Run Code Online (Sandbox Code Playgroud)

让我们解决这个问题。

设置类型

我将使用一个新的 APIcreateTypes来为每个 json 注册一个新接口和 2 种类型。需要注意的是JsonNode同时包含常见的领域OneTwo

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    interface JsonNode {
      name: String
      type: String!
    }

    type One implements Node & JsonNode {
      name: String
      type: String!
      food: String
    }

    type Two implements Node & JsonNode {
      name: String
      type: String!
      game: String
    }
  `
  createTypes(typeDefs)
}
Run Code Online (Sandbox Code Playgroud)

神奇发生在这一行,其中One&Two实现了JsonNode(自定义接口)和Node(Gatsby 的接口)。

type One implements Node & JsonNode { ... }
Run Code Online (Sandbox Code Playgroud)

现在我可以编写一个实现的片段,JsonNode它适用于两种类型。

// blogPostTemplate.js
import React from "react"
import { graphql } from "gatsby"

export default ({ data }) => <div>{JSON.Stringify(data)}</div>

export const name = graphql`
  fragment name on JsonNode {
    name
    level
  }
`

export const pageQuery = graphql`
  query {
    one {
      ...name <-  works
    }
    two {
      ...name <-  works
    }
  }
`
Run Code Online (Sandbox Code Playgroud)

这需要一些设置,但如果您提前知道您的数据类型并且需要大量重用片段,那么这可能是值得的。