Apollo graphql 正在为 mongodb 中的空子文档返回空数据

Posted

技术标签:

【中文标题】Apollo graphql 正在为 mongodb 中的空子文档返回空数据【英文标题】:Apollo graphql is returning null data for an empty subdocument in mongodb 【发布时间】:2019-10-15 10:33:43 【问题描述】:

我在我的 apollo graphql 突变中添加了另一个字段(联系人),但是在检索该字段时它会产生错误数据。即使匹配的 mongodb 字段是否有真实数据,graphql 也会返回具有空值的多个字段。

我已经尝试在 apollo 客户端 graphql 选项中删除 __typename ,但它没有帮助。当前堆栈是:

reactjs 16.8.5
react-apollo 2.5.2
mongodb 3.6.11
mongoose 5.3.6
apollo-server 2.3.1 

我不明白为什么会这样,因为我有另一个几乎相同但返回正确的子文档。

// CLIENTSIDE
// APOLLO SETUP
const cache = new InMemoryCache(
   dataIdFromObject: object => object.key || null
 )

 const AuthLink = (operation, forward) => 
   const token = cookies._uid
   operation.setContext(context => (
     ...context,
     headers: 
       ...context.headers,
       authorization: token
     
   ))

   return forward(operation)
 

 const httpLink = new HttpLink(
   uri:"/graphql",
   credentials: "include"
 )

// mutation
 export const LOGIN_MUTATION = gql`
  mutation loginMutation($email: String!, $password: String!) 
    login(input: email: $email, password: $password) 
      token
      user 
        _id
        contacts   // This is the subfield in question
          _id
          username
        
        subscriptions   // This subfield returns corretly
          _id
          title
          levels 
            _id
          
        
      
      error 
        path
        message
      
    
  
`

// SERVERSIDE
// APOLLO SETUP
 const baseSchema = `
   schema 
     query: Query,
     mutation: Mutation
   
 `
 const schema = makeExecutableSchema(
   typeDefs: [
     userTypeDefs,
   ],
   resolvers: merge(
     ,
     userResolvers,
   )
 )

 const ObjectId = mongoose.Types
 ObjectId.prototype.valueOf = function() 
  return this.toString()
 

 export default new ApolloServer(
   introspection: true,
   credentials: true,
   schema,
   context: (req, res) => (
     url: req.protocol + "://" + req.get("host"), 
     req
   )
 )

// USER MONGOOSE MODEL
 export const UserSchema = new mongoose.Schema(
   
     contacts: [
       
         type: mongoose.Schema.Types.ObjectId,
         ref: "User"
       
     ],     
     firstName: 
       type: String
     ,     
     lastName: 
       type: String
     ,     
     username: 
       type: String,
       lowercase: true,
       unique: true,
       required: [true, "can't be blank"],
       match: [/^[a-zA-Z0-9]+$/, "is invalid"],
       index: true
     ,

     sentRequests: [
       
         username: 
           type: String,
           default: ""
         
       
     ],     
     subscriptions: [
       
         type: mongoose.Schema.Types.ObjectId,
         ref: "Course"
       
     ],
     password: 
       default: "",
       required: [true, "can't be blank"],
       type: String
     
   ,
   timestamps: true
 )

 UserSchema.plugin(uniqueValidator, message: "is already taken.")
 export default mongoose.model("User", UserSchema)

// USER GRAPHQL SCHEMA
type User 
   _id: ID
   contacts: [User]
   email: String!
   username: String
   subscriptions: [Course]
   createdAt: String
   updatedAt: String
 

 type AuthPayload 
   token: String
   user: User
   error: [Error]
 

 input LoginInput 
   email: String!
   password: String!
 

 type Mutation 
   login(input: LoginInput): AuthPayload
 

// USER RESOLVER
 const login = async (parent, args, ctx, info) =>    
   let email = args.email

   let user = await User.findOne(email)
     .populate("subscriptions")
     .populate("contacts")
     .exec()

  if (!user) 
     arrayOfErrors.push(
       path: "email",
       message: "invalid email"
     )
    else if (user) 

   console.log("user: ", user)


   return 
     user,
     error: arrayOfErrors
   
 

 Mutation: 
   login
 
// CONSOLE LOG CLIENTSIDE
user:
  confirmed: true
  contacts: Array(3)  // returns an array of 3 items??  It's [] in mongodb
    0: _id: null, username: null
    1: _id: null, username: null
    2: _id: null, username: null
    length: 3
    __proto__: Array(0)
  subscriptions: Array(1)
    0: _id: "5cd36fc1c8d1e222b99d9c58", title: "Korean Class 101 Study", 
    length: 1
    __proto__: Object
__proto__: Object

// CONSOLE LOG SERVERSIDE
POST /graphql 200 64.359 ms - -
user: 
  contacts: [ ],
  subscriptions:
   [  _id: 5cd6f7f8212af32c75555d4b,
       subscribers: 2,
       owner: 5cd36edec8d1e222b99d9c57,
       createdAt: 2019-05-09T00:09:37.845Z,
       updatedAt: 2019-05-11T16:36:14.661Z,
       __v: 23  ],
  password: '$2b$10$bkjiazklcoqlkJSJSAioxoAqkoajsdjnqjkiaallaadfadfp7zS',
  _id: 5cd36edec8d1e222b99d9c57,
  email: 'example@test.com',
  username: 'example',
  createdAt: 2019-05-09T00:05:50.314Z,
  updatedAt: 2019-05-29T17:23:21.545Z,
  __v: 32

我希望一个空数组返回 [ ],而不是 graphql 返回一个包含 3 个具有空值的项的数组。即使我将真实联系人添加到数据库中,它仍会返回 3 个具有空值的项目。 订阅返回正确,但它几乎与联系人字段相同,只是在 mongoose 模型中是“课程”类型而不是“用户”类型。

【问题讨论】:

【参考方案1】:

我怀疑有人会遇到这种类型的错误,但如果您遇到这种情况,这里有一个解决方案。在开发过程中,我将此代码放在解析器中以进行测试。删除它解决了这个问题。

// USER RESOLVER
...
Query: ...,

/* code to remove
User: 
  contacts: user => 
    return ["Mo", "Larry", "Curly"]
  

*/ 

Mutation: ...

一件非常微不足道的事情,但几个月没有出现在这个实际代码中,所以很难发现。我从没想过这三个臭皮囊会让我这么头疼。

【讨论】:

以上是关于Apollo graphql 正在为 mongodb 中的空子文档返回空数据的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Redux 应用程序转换为 GraphQL/Apollo?

Graphql 查询和 Apollo

Apollo GraphQL:为 HTTPBatchedNetworkInterface 设置端口?

使用 graphql 和 apollo 客户端刷新 Angular 令牌

使用 Django、GraphQL、Apollo 和 VueJS 进行 URL 管理

Apollo GraphQL DataLoader DynamoDb