如何管理可以在输出类型中为空的 GraphQL 子对象类型?

How to manage GraphQL child objectType that can be nullable in an output type?

我正在设置一个 nodeJS GraphQL API 并且我正在试验关于我的一种资源输出类型的阻塞点。

该特征是一个包含三个不同级别的表单:

我的 GraphQL 资源正在 returning 数据库中的所有模板,因此它是一个数组,对于每个模板 returning 他的所有项目和 "question" 需要的每个类型的项目return 包含相关问题的数组。

我的问题是:我真的不知道如何 return 类型不同于 "question" 的 formItems 的空对象类型,或者对于这种类型是否有更好的方法情况

我尝试查看 GraphQL 指令和内联片段,但我认为它确实需要由后端管理,因为它对 API 消费者是透明的。

const formTemplate = new GraphQLObjectType({
  name: 'FormTemplate',
  fields: () => {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      authorId: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      name: {
        type: new GraphQLNonNull(GraphQLString)
      },
      items: {
        type: new GraphQLList(formItem),
        resolve: parent => FormItem.findAllByTemplateId(parent.id)
      }
    }
  }
})

const formItem = new GraphQLObjectType({
  name: 'FormItem',
  fields: () => {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      templateId: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      type: {
        type: new GraphQLNonNull(GraphQLString)
      },
      question: {
        type: formQuestion,
        resolve: async parent => FormQuestion.findByItemId(parent.id)
      }
    }
  }
})

const formQuestion= new GraphQLObjectType({
  name: 'FormQuestion',
  fields: () => {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      itemId: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      type: {
        type: new GraphQLNonNull(GraphQLString)
      },
      label: {
        type: new GraphQLNonNull(GraphQLString)
      }
    }
  }
})

我的 GraphQL 请求:

query {
  getFormTemplates {
    name
    items {
      type
      question {
        label
        type
      }
    }
  }
}

我期望的是

{
  "data": {
    "getFormTemplates": [
      {
        "name": "Form 1",
        "items": [
          {
            "type": "question",
            "question": {
              "label": "Question 1",
              "type": "shortText"

          },
          {
            "type": "rawContent"
            "question": {}
          }
        ]
      }
    ]
  }
}

我会设计您的 "level 2" 项目,以便 "type" 属性 对应于实际的 GraphQL 类型,实现通用接口。另外,一般来说,我会设计架构,使其具有指向相邻项目的实际链接,而不是它们的标识符。

因此,如果每个表单项都可能有一个关联的模板,您可以将其设为 GraphQL interface:

interface FormItem {
  id: ID!
  template: FormTemplate
}

那么你的三种物品可以有三种不同的类型

# Skipping VideoItem
type ImageItem implements FormItem {
  id: ID!
  template: FormTemplate
  src: String!
}
type QuestionItem implements FormItem {
  id: ID!
  template: FormTemplate
  questions: [FormQuestion!]!
}

您描述的其他类型是:

type FormTemplate {
  id: ID!
  author: Author!
  name: String!
  items: [FormItem!]!
}
type FormQuestion {
  id: ID!
  question: Question
  type: String!
  label: String!
}

另一件棘手的事情是,由于并非所有表单项都是问题,因此您必须在查询中特别提及您对问题感兴趣,以获取特定于问题的字段。您的查询可能类似于

query {
  getFormTemplates {
    name
    items {
      __typename # a GraphQL builtin that gives the type of this object
      ... on Question {
        label
        type
      }
    }
  }
}

... on Question 语法是一种 inline fragment,您可以类似地使用它来挑选特定于其他类型表单项的字段。

谢谢大卫的回答!

我已经弄清楚如何使用似乎最适合此用例的内联片段和 UnionType 来解决我的问题。这是代码:

const formItemObjectType = new GraphQLUnionType({
  name: 'FormItemObject',
  types: [formItemContent, formItemQuestion],
  resolveType(parent) {
    switch (parent.type) {
      case ('question'): return formItemQuestion
      default: return formItemContent
    }
  }
})

以及使用内联片段的 GraphQL 查询:

query {
  getFormTemplates {
    name
    items {
      ...on FormItemContent {
        type,
        meta
      }
      ...on FormItemQuestion {
        type,
        meta,
        question {
           label
        }
      }
    }
  }
}