节点 - GraphQL - Ad.user 字段类型必须是输出类型但得到:未定义

Node - GraphQL - Ad.user field type must be Output Type but got: undefined

我似乎遇到了在 AdType 中使用 UserType 的循环依赖问题。

这是我的用户类型文件:UserType

这是我的 AdType 文件:AdType

如果我尝试使用下面的代码,即使我正确导入了 UserType,也会收到错误 "Ad.user field type must be Output Type but got: undefined"。

import { UserType } from '../User/user.graphql.model'
import { UserSchema } from '../User/user.mongoose.model'

const user =  {
  type: UserType,
  resolve(parentValue, args) {
    return UserSchema.findById(parentValue.user);
  }
};

//------------------------------
// Ad Type
//------------------------------
export const AdType = new GraphQLObjectType({
  name: 'Ad',
  fields: () => ({
    id,
    user,
    title,
    views,
    availability,
... more code

如果我在导入后尝试在 AdType 中控制台记录 UserType,它显示未定义,但是当我将其用作:

//------------------------------
// Ad Type
//------------------------------
export const AdType = new GraphQLObjectType({
  name: 'Ad',
  fields: () => ({
    id,
    user: {
      type: UserType,
      resolve(parentValue, args) {
        return UserSchema.findById(parentValue.user);
      }
    },
    title,
... more code

它按预期工作,只是不允许我提取代码来分隔常量。 我以相同方式导入和使用的所有其他类型都按预期工作,将 Ads 导入 Users 也可以正常工作,但将 User 导入 Ads 似乎会中断。两者的代码基本相同,只是信息不同。

I am already using fields: () => ( { } ) to lazily load fields to avoid problems with circular dependencies, so this problem is really banging against my head.

但是你做的不对。 Javascript 没有惰性评估。这意味着 user 的值不是在调用函数时确定的,而是在计算 const 变量定义时确定的。此时变量 UserType 没有值,因此未定义。您的对象定义需要在调用函数时发生。如果仍然不清楚,我可以详细说明您的类型是如何解析的。

尝试内联定义用户类型或使其成为一个函数:

const user = () => ({ type: UserType, /* ... */ })

export const AdType = new GraphQLObjectType({
  name: 'Ad',
  fields: () => ({
    id,
    user: user(),
    title,
    views,
    availability,

我不确定你为什么把你的字段拉到单独的常量中,你的代码看起来并没有那么大,它提高了可读性,但当然我可能是错的。

好吧,让我们看看模块是如何解析的。为了使这更容易,我使用 CJS,因为您很可能无论如何都将代码转译下来,而 ES 模块正慢慢进入节点。

// user.graphql.model.js
const adModule = require('ad.graphql.model.js');

// Node tries to resolve ad.graphql.model.js
const userModule = require('user.graphql.model.js');
// Ups, this one has been resolved already and required this as dependency.
// We have no other choice than to assign an empty object here
// userModule is {}
const user =  {
  type: userModule.UserType, // undefined
  resolve(parentValue, args) {
    return UserSchema.findById(parentValue.user);
  }
};
// finish this module and return to user.graphql.model.js
// adModule now contains the resolved module
const adModule = require('ad.graphql.model.js');
// finish module and replace {} with actual module content in ad.graphql.model.js
// userModule contains UserType
const userModule = require('user.graphql.model.js');

const ads = {
  type: new GraphQLList(asModule.AdType), // GraphQLObjectType
}

// Now your Schema does build/inits itself after the config has been specified
// (and all modules have been resolved)
// imagine user being a function now taht is called on Schema init
const user = () => ({
  type: userModule.UserType, // GraphQLObjectType
  /* ... */
})