Ошибка GraphQL: ожидается, что undefined будет типом GraphQL - PullRequest
0 голосов
/ 18 февраля 2020

Я сталкивался с этой ошибкой GraphQL в течение последнего дня или около того, и мне трудно определить, в чем проблема. Я надеюсь, что следующее имеет смысл ...

В моем проекте у меня есть следующие типы GraphQL; Анкета и вопрос, в котором Анкета содержит много Вопросов, и Вопрос принадлежит одной Анкете.

У меня есть следующие запросы: getQuestionnaires (получает все Анкеты), getQuestionnaire (получает одну Анкету по ее идентификатору ), getQuestions (получает все вопросы, либо все вопросы в системе, либо вопросы к вопроснику, указав questionnaireId).

Это тип вопросника (я оставил несколько атрибутов для краткости):

import {
  GraphQLID,
  GraphQLObjectType,
  GraphQLNonNull,
} from 'graphql'
import QuestionType from './Question'
import QuestionResolver from '../resolver/question'

const QuestionnaireType: GraphQLObjectType = new GraphQLObjectType({
  name: 'Questionnaire',
  fields() {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLID),
      },

      // other literal attributes go here

      questions: QuestionResolver.query.getQuestions,
    }
  },
})

export default QuestionnaireType

Как видите, атрибут questions для Типа вопросника просто вызывает распознаватель для getQuestions.

Это распознаватель для getQuestions :

import {
  GraphQLList,
  GraphQLID
} from 'graphql'

import QuestionType from '../../type/Question'

export default {
  type: GraphQLList(QuestionType),
  description: 'Returns a list of questions',
  args: {
    questionnaireId: {
      type: GraphQLID,
      description: 'Questions that belong to this questionnaire',
    },
  },
  async resolve(source: any, { questionnaireId = undefined }: any, ctx: any): Promise<any> {
    // Based on the `questionnaireId` get the questions. If `undefined` get "all" questions.
  },
}

Как видите, getQuestions возвращает GraphQLList(QuestionType). Что приводит нас к типу вопроса:

import {
  GraphQLID,
  GraphQLObjectType,
  GraphQLNonNull,
} from 'graphql'
import QuestionnaireType from './Questionnaire'
import QuestionnaireResolver from '../resolver/questionnaire'

const QuestionType: GraphQLObjectType = new GraphQLObjectType({
  name: 'Question',
  fields() {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLID),
      },

      questionnaire: QuestionnaireResolver.query.getQuestionnaire,
    }
  },
})

export default QuestionType

Хорошо. Все идет нормально. Этот код компилируется и запускается.

Однако с этим кодом есть проблема. Решатель getQuestions принимает идентификатор вопросника в качестве параметра (questionnaireId). Если идентификатор анкеты не передан (undefined), он просто возвращает все вопросы в системе. Это означает, что в типе вопросника для атрибута questions мы не хотим напрямую вызывать распознаватель getQuestions, но мы хотим обернуть его в распознаватель, который извлекает source.id (source - это Анкета в этом контексте) и передайте ее в качестве аргумента для распознавателя getQuestions. Если мы этого не сделаем, то при запросе вопросов в вопроснике всегда будут возвращаться все вопросы в системе, а это то, что нам не нужно.

Итак, мы заключаем решатель в решатель, который извлекает Идентификатор вопросника перед вызовом распознавателя getQuestions. Наш тип вопросника теперь выглядит следующим образом:

import {
  GraphQLID,
  GraphQLObjectType,
  GraphQLNonNull,
} from 'graphql'
import QuestionType from './Question'
import QuestionResolver from '../resolver/question'

const QuestionnaireType: GraphQLObjectType = new GraphQLObjectType({
  name: 'Questionnaire',
  fields() {
    return {
      id: {
        type: new GraphQLNonNull(GraphQLID),
      },

      // other literal attributes go here

      questions: {
        type: GraphQLList(QuestionType),
        async resolve(source: any, args: any, ctx: any): Promise<any> {
          return QuestionResolver.query.getQuestions.resolve(
            source,
            {
              questionnaireId: source.questionnaireId,
            },
            ctx
          )
        },
      },
    }
  },
})

export default QuestionnaireType

Теперь мы завернули решатель getQuestions, чтобы извлечь source.id (т. Е. Идентификатор вопросника), чтобы мы всегда могли передать его в getQuestions resolver (так как это то, что мы всегда хотим в этом контексте).

Однако, когда я делаю выше. Я получаю эту ошибку:

[Node] /Project/api/node_modules/graphql/type/definition.js:91
[Node]     throw new Error("Expected ".concat((0, _inspect.default)(type), " to be a GraphQL type."));
[Node]     ^
[Node]
[Node] Error: Expected undefined to be a GraphQL type.
[Node]     at assertType (/Project/api/node_modules/graphql/type/definition.js:91:11)
[Node]     at new GraphQLList (/Project/api/node_modules/graphql/type/definition.js:307:19)
[Node]     at Object.GraphQLList (/Project/api/node_modules/graphql/type/definition.js:309:12)
[Node]     at Object.<anonymous> (/Project/api/build/graphql/resolver/question/getQuestions.js:11:21)
[Node]     at Module._compile (internal/modules/cjs/loader.js:936:30)
[Node]     at Object.Module._extensions..js (internal/modules/cjs/loader.js:947:10)
[Node]     at Module.load (internal/modules/cjs/loader.js:790:32)
[Node]     at Function.Module._load (internal/modules/cjs/loader.js:703:12)
[Node]     at Module.require (internal/modules/cjs/loader.js:830:19)
[Node]     at require (internal/modules/cjs/helpers.js:68:18)

Я в тупике ....

В строке 11 из getQuestions есть:

08: export default {
09:   type: GraphQLList(QuestionType),
10:   description: 'Returns a list of questions',
11:   args: {
12:     questionnaireId: {
13:       type: GraphQLID,
14:       description: 'Questions that belong to this questionnaire',
15:     },

Я думаю, что у меня есть буквально перепробовал все, что приходит на ум, чтобы найти проблему. Мой проект biggi sh, и я использую эту технику в нескольких других местах без проблем, но по какой-то причине в этом конкретном сценарии она не компилируется.

Единственный способ, который я имею на данный момент, это использовать что-то вроде этого в распознавателе getQuestions:

import {
  GraphQLList,
  GraphQLID
} from 'graphql'

import QuestionType from '../../type/Question'

export default {
  type: GraphQLList(QuestionType),
  description: 'Returns a list of questions',
  args: {
    questionnaireId: {
      type: GraphQLID,
      description: 'Questions that belong to this questionnaire',
    },
  },
  async resolve(source: any, { questionnaireId = undefined }: any, ctx: any): Promise<any> {

    // The workaround
    const id = questionnaireId || source.id
  },
}

Однако распознаватель getQuestions не обязан смотреть на source для извлечения аргументов, а также source может технически быть другим объектом, чем Анкета, добавляющая больше сложности к распознавателю getQuestions.

Любая помощь и понимание приветствуются.

1 Ответ

0 голосов
/ 18 февраля 2020

Вам нужно посмотреть на скомпилированный код, чтобы отследить ошибку. Однако, основываясь на трассировке стека, похоже, что QuestionType оценивается как undefined. Это может произойти, когда у вас есть циклические зависимости (то есть модуль A, который импортирует модуль B, который импортирует модуль A et c.).

Как правило, следует избегать вызова других распознавателей внутри распознавателя для начала. Если у вас много дубликатов, общий лог c может быть извлечен в общую функцию, которая может быть вызвана из обоих распознавателей. Это имеет место, когда наши резольверы содержат бизнес-логики c. Хорошей практикой является попытка сделать ваши резольверы максимально простыми, изолируя бизнес-логики c внутри отдельных моделей или служб, чьи методы затем можно вызывать от ваших распознавателей.

Трудно понять, как исправить круговую зависимость, не видя весь соответствующий код. Гарантированный способ разрешения любых циклических зависимостей заключается в замене операторов импорта для Dynami c require s внутри функции fields. Тем не менее, вы можете не найти это решение особенно элегантным. Хорошим первым шагом может быть консолидация некоторых из ваших модулей - например, создание единого модуля «Вопрос», который включает тип «Вопрос» и все связанные поля «Запрос» и «Мутация».

...