Как управлять дочерним объектным типом GraphQL, который может быть обнуляем в типе вывода?
Я устанавливаю API-интерфейс GraphQL для nodeJS и экспериментирую с точкой блокировки одного из типов вывода моего ресурса.
Функция представляет собой форму, которая содержит три разных уровня:
- Уровень 1- formTemplate
- Уровень 2- formItems (templateId, тип (видео, изображение, вопрос) - 1-N отношение с formTemplate)
- Уровень 3- formQuestions (0-1 отношение с formItem тогда и только тогда, когда formItems.type равен 'question')
Мой ресурс GraphQL возвращает все шаблоны в базе данных, так что это массив, который для каждого шаблона возвращает все свои элементы, и каждый элемент типа "вопрос" должен возвращать массив, содержащий связанный вопрос.
Моя проблема: я действительно не знаю, как вернуть пустой тип объекта для 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": {}
}
]
}
]
}
}
2 ответа
Спасибо Дэвид за ваш ответ!
Я понял, как решить мою проблему, используя встроенные фрагменты и UnionTypes, которые кажутся наиболее приспособленными для этого варианта использования. Вот код:
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
}
}
}
}
}
Я бы спроектировал элементы "уровня 2" так, чтобы свойство "тип" соответствовало фактическим типам GraphQL, реализуя общий интерфейс. Кроме того, в общем, я бы разработал схему так, чтобы она содержала реальные ссылки на соседние элементы, а не их идентификаторы.
Так что, если у каждого элемента формы есть связанный шаблон, вы можете сделать его интерфейсом GraphQL:
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
синтаксис является встроенным фрагментом, и вы можете аналогичным образом использовать его для выбора полей, характерных для других видов элементов формы.