简体   繁体   English

GraphQL 错误:预期未定义为 GraphQL 类型

[英]GraphQL Error: Expected undefined to be a GraphQL type

I have been running into this GraphQL error for the past day or so and I have a hard time pinpointing where the problem lies.在过去一天左右的时间里,我一直遇到这个 GraphQL 错误,我很难找出问题所在。 I hope the following makes sense...我希望以下是有道理的......

In my project I have the following GraphQL types;在我的项目中,我有以下 GraphQL 类型; Questionnaire & Question were a Questionnaire has many Questions and a Question belongs to a single Questionnaire.问卷和问题是问卷有很多问题,一个问题属于一个问卷。

I have the following queries: getQuestionnaires (gets all Questionnaires), getQuestionnaire (gets a single Questionnaire by its Id), getQuestions (gets all Questions, either all questions in the system or the questions for a Questionnaire by specifying the questionnaireId ).我有以下查询: getQuestionnaires (获取所有问卷)、 getQuestionnaire (通过其 Id 获取单个问卷)、 getQuestions (获取所有问题,系统中的所有问题或通过指定questionnaireId ID 的questionnaireId )。

This is the Questionnaire Type (I have left out a bunch of attributes for brevity):这是问卷类型(为简洁起见,我省略了一堆属性):

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

As you can see, the questions attribute for the Questionnaire Type simply calls the resolver for getQuestions .如您所见, Questionnaire Type 的questions属性只是调用getQuestions的解析器。

This is the resolver for 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.
  },
}

As you can see, the getQuestions returns a GraphQLList(QuestionType) .如您所见, getQuestions返回一个GraphQLList(QuestionType) Which brings us to the Question Type:这将我们带到了问题类型:

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

Okay.好的。 So far so good.到现在为止还挺好。 This code compiles and runs.此代码编译并运行。

However, there is a problem with this code.但是,此代码存在问题。 The getQuestions resolver takes in the Questionnaire Id as a parameter ( questionnaireId ). getQuestions解析器将 Questionnaire Id 作为一个参数 ( questionnaireId )。 If the Questionnaire Id is not passed ( undefined ) it simply returns all Questions in the system.如果问卷 ID 未通过( undefined ),它只会返回系统中的所有问题。 This means that in the Questionnaire Type, on the questions attribute, we don't want call the getQuestions resolver directly, but we want to wrap it in a resolver that extracts the source.id (the source is the Questionnaire in this context) and pass it as an argument to the getQuestions resolver.这意味着,在问卷的类型,对questions的属性,我们不想叫getQuestions直接解析器,但我们希望把它包在一个解析器提取的source.id (该source是调查问卷在这方面)和将其作为参数传递给getQuestions解析器。 If we don't do this, then querying for questions on a Questionnaire will always return all Questions in the system, which is what we don't want.如果我们不这样做,那么在问卷上查询问题将始终返回系统中的所有问题,这是我们不想要的。

So, we wrap the resolver in a resolver that extracts the Questionnaire Id before calling the getQuestions resolver.因此,我们将解析器包装在一个解析器中,该解析器在调用getQuestions解析器之前提取问卷 ID。 Our Questionnaire Type now looks like this:我们的问卷类型现在看起来像这样:

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

We have now wrapped the getQuestions resolver to extract the source.id (ie the Questionnaire Id) so we can always pass it on to the getQuestions resolver (since that is what we always want in this context).现在,我们已经包裹getQuestions解析器提取source.id (即问卷编号),所以我们总是可以把它传递给getQuestions解析器(因为这是我们一直想在这方面)。

However, when I do the above.但是,当我执行上述操作时。 I get this error:我收到此错误:

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

I'm stumped....我难住了......

On line 11 of getQuestions is:getQuestions第 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:     },

I think I have literally tried everything that comes to mind to find the problem.我想我已经尝试了所有想到的方法来找到问题。 My project is biggish and I use this technique in several other places without problems but for some reason, in this particular scenario it fails to compile..我的项目很大,我在其他几个地方使用这种技术没有问题,但由于某种原因,在这种特殊情况下它无法编译..

The only workaround I have at the moment is to use something like this in the getQuestions resolver:我目前唯一的解决方法是在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> {

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

However, it's not the responsibility of the getQuestions resolver to look at the source to extract arguments and also, the source could technically be another object than a Questionnaire adding more complexity to the getQuestions resolver.但是,查看source以提取参数并不是getQuestions解析器的责任,而且,从技术上讲, source可能是另一个对象,而不是问卷,从而增加了getQuestions解析器的复杂性。

Any help and insight appreciated.任何帮助和见解表示赞赏。

You need to look at the compiled code to trace the error.您需要查看编译后的代码以跟踪错误。 Based on the stack trace, though, it looks like QuestionType is evaluating to undefined .但是,根据堆栈跟踪,看起来QuestionType正在评估为undefined This can happen when you have circular dependencies (ie module A which imports module B which imports module A etc.).当您有循环依赖(即模块 A 导入模块 B 导入模块 A 等)时,可能会发生这种情况。

You should generally avoid calling other resolvers inside a resolver to begin with.您通常应该避免在解析器中调用其他解析器。 If you have a lot of duplication, the shared logic can be extracted to a common function that can be called from both resolvers.如果您有很多重复,则可以将共享逻辑提取到可以从两个解析器调用的公共函数中。 There's a tendency for this to happen when our resolvers contain business logic -- it's a good practice to try to keep your resolvers as lean as possible, isolating business logic inside separate models or services whose methods can then be called from your resolvers.当我们的解析器包含业务逻辑时,就会出现这种情况——尝试使您的解析器尽可能精简,将业务逻辑隔离在单独的模型或服务中,然后可以从您的解析器调用其方法,这是一种很好的做法。

It's hard to know how to fix your circular dependency without seeing all the relevant code.如果没有看到所有相关代码,很难知道如何修复循环依赖。 The guaranteed way to resolve any circular dependencies is to swap out your import statements for dynamic require s inside the fields function.解决任何循环依赖的有保证的方法是将您的导入语句换成fields函数内的动态require However, you may not find that solution particularly elegant.但是,您可能不会发现该解决方案特别优雅。 A good first step may be to consolidate some of your modules -- for example, creating a single Question module that includes the Question type and all related Query and Mutation fields.一个好的第一步可能是合并您的一些模块——例如,创建一个包含问题类型和所有相关查询和变异字段的问题模块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM