Как использовать фрагмент GraphQL на нескольких типах

Я использую Contenful в качестве источника для страниц, постов и вики-статей сайта Gatsby. Мои запросы для страниц и вики-статей выглядят очень похоже:

PageBySlug

export const pageQuery = graphql`
  query PageBySlug($slug: String!) {
    site {
      meta: siteMetadata {
        title
        url: siteUrl
      }
    }
    page: contentfulPage(slug: {eq: $slug}) {
      title {
        title
      }
      slug
      body {
        data: childMarkdownRemark {
          excerpt
          html
          headings {
            value
            depth
          }
        }
      }
      created: createdAt(formatString: "D. MMMM YYYY", locale: "de")
      updated: updatedAt(formatString: "D. MMMM YYYY", locale: "de")
      authors: author {
        name
        email
      }
    }
  }
`

WikiArticleBySlug

export const articleQuery = graphql`
  query WikiArticleBySlug($slug: String!) {
    site {
      meta: siteMetadata {
        title
        url: siteUrl
      }
    }
    article: contentfulWikiArticle(slug: {eq: $slug}) {
      title {
        title
      }
      slug
      body {
        data: childMarkdownRemark {
          html
          headings {
            value
            depth
          }
        }
      }
      created: createdAt(formatString: "D. MMMM YYYY", locale: "de")
      updated: updatedAt(formatString: "D. MMMM YYYY", locale: "de")
      authors: author {
        name
        email
      }
      section {
        title
        slug
      }
      subsection {
        title
        slug
      }
    }
  }
`

За исключением дополнительного раздела и подраздела в вики-статьях, запросы идентичны. Чтобы сохранить вещи СУХОЙ, как я могу передать как можно больше каждого запроса в отдельный фрагмент, несмотря на то, что работал с несколькими типами?

fragment pageFields on ContenfulPage, ContenfulWikiArticle {
  ...
}

явно не работает.

0 ответов

Недавний выпуск Gatsby позволяет пользователям устанавливать собственные типы для схемы graphql, что делает этот вопрос окончательно возможным.

Это всегда было возможно с GraphQl, если пользователи имеют контроль над схемой, но благодаря недавнему обновлению Gatsby, пользователи, наконец, могут реализовать это самостоятельно.

Настроить

Чтобы настроить простой пример, я буду использовать gatsby-transformer-json в такой простой папке

jsonFolder
  |--one.json { "type": "One", "name": "a", "food": "pizza" }
  `--two.json { "type": "Two", "name": "b", "game": "chess" }

и используйте опцию для объявления моего имени типа:

{
  resolve: `gatsby-transformer-json`,
  options: { 
    typeName: ({ object }) => object.type,
  },
},

Теперь у меня есть два типа, которые были созданы для меня. Я могу создать фрагмент на одном из них, но не на обоих:

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

export const pageQuery = graphql`
  query {
    one {
      ...name
    }
    two {
      ...name <-- ⚠️ throw type error
    }
  }
`

Давайте это исправим.

Настройка типов

Я буду использовать новый API под названием createTypes зарегистрировать новый интерфейс и 2 типа для каждого из JSON. Обратите внимание, что JsonNode содержит общие поля обоих One а также Two:

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)
}

Волшебство происходит на этой линии, где One & Two реализует оба JsonNode (пользовательский интерфейс) и Node (Интерфейс Гэтсби).

type One implements Node & JsonNode { ... }

Теперь я могу написать фрагмент, который реализует 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
    }
  }
`

Это требует небольшой настройки, но, возможно, оно того стоит, если вы заранее знаете свой тип данных и вам нужно многократно использовать фрагменты.

Другие вопросы по тегам