Graphql apollo 客户端从 AppSync 返回空值

Posted

技术标签:

【中文标题】Graphql apollo 客户端从 AppSync 返回空值【英文标题】:Graphql apollo client returning null value from AppSync 【发布时间】:2019-01-04 00:54:22 【问题描述】:

我有一个 AppSync api 设置,具有特定的突变设置。当我在 AppSync 中测试该突变时,效果很好。但是,当我尝试在反应应用程序中使用相同的查询时,我得到一个空值。但是,当我在浏览器中查看网络选项卡时,AppSync 正在返回正确的数据。

我的 React 应用中有以下突变:

import gql from 'graphql-tag';

export default gql`
mutation CreateUser(
  $email: String!,
  $name: String
) 
  createUser(input: 
    email: $email
    name: $name
  ) 
    __typename
    id
  

`;

我的 React 组件(为简洁起见略有删节):

import React,  Component  from 'react';
import MutationCreateUser from '../GraphQL/MutationCreateUser';
import  graphql  from "react-apollo";

class UserForm extends Component 

  state = 
    name: '',
    email: '',
    ... 
    err: null
  

  ...

  submit = async () => 
    const  createUser, history  = this.props;
    const user = this.state;
    try 
      const result = await createUser(
        name: user.name,
        email: user.email
      );
      // This shows  "data":  "createUser": "null"  
      console.log('RES', result);
     catch (e) 
      this.setState( err: e.message );
    
    ... 
  
  ... 


export default graphql(
  MutationCreateUser,
  
    props: ( mutate ) => 
      return 
        createUser: (user) =>           
          return mutate(
            variables: user
          );
        
      
    
  
)(UserForm);

当我在网络选项卡中检查查询时,我看到: "data":"createUser":"__typename":"User","id":"860b7cec-e882-4242-aca0-d4865154b640"

但是,在我的 React 组件中,我看到: "data": "createUser": "null"

我不确定我是否遗漏了有关如何使用 Apollo 设置组件的内容,这意味着数据未正确加载。但查询本身似乎工作正常。

数据也按预期正确存储在 DynamoDB 中。

这是我的请求映射:


  "version": "2017-02-28",
  "operation": "PutItem",
  "key": 
    "id": $util.dynamodb.toDynamoDBJson($util.autoId()),
  ,
  "attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
  "condition": 
    "expression": "attribute_not_exists(#id)",
    "expressionNames": 
      "#id": "id",
    ,
  ,

我的响应映射:

$util.toJson($ctx.result)

最后是我的架构:

input CreateQuestionInput 
    text: String!
    sectionId: ID!


input CreateScoreInput 
    score: Int!
    questionId: ID!
    userId: ID!


input CreateSectionInput 
    title: String
    subSection: String


input CreateUserInput 
    email: String!
    name: String
    jobTitle: String
    jobTitleShare: Boolean
    department: String
    level: Int
    yearRange: Int
    industry: String
    orgSize: Int


input DeleteQuestionInput 
    id: ID!


input DeleteScoreInput 
    id: ID!


input DeleteSectionInput 
    id: ID!


input DeleteUserInput 
    id: ID!


type Mutation 
    createSection(input: CreateSectionInput!): Section
    updateSection(input: UpdateSectionInput!): Section
    deleteSection(input: DeleteSectionInput!): Section
    createScore(input: CreateScoreInput!): Score
    updateScore(input: UpdateScoreInput!): Score
    deleteScore(input: DeleteScoreInput!): Score
    createQuestion(input: CreateQuestionInput!): Question
    updateQuestion(input: UpdateQuestionInput!): Question
    deleteQuestion(input: DeleteQuestionInput!): Question
    batchCreateQuestion(questions: [CreateQuestionInput]!): [Question]
    createUser(input: CreateUserInput!): User
    updateUser(input: UpdateUserInput!): User
    deleteUser(input: DeleteUserInput!): User


type Query 
    getSection(id: ID!): Section
    listSections(filter: TableSectionFilterInput, limit: Int, nextToken: String): SectionConnection
    getScore(id: ID!): Score
    listScores(filter: TableScoreFilterInput, limit: Int, nextToken: String): ScoreConnection
    getQuestion(id: ID!): Question
    listQuestions(filter: TableQuestionFilterInput, limit: Int, nextToken: String): QuestionConnection
    getUser(id: ID!): User
    listUsers(filter: TableUserFilterInput, limit: Int, nextToken: String): UserConnection


type Question 
    id: ID!
    text: String!
    sectionId: ID!


type QuestionConnection 
    items: [Question]
    nextToken: String


type Schema 
    query: Query


type Score 
    id: ID!
    score: Int!
    questionId: ID!
    userId: ID!


type ScoreConnection 
    items: [Score]
    nextToken: String


type Section 
    id: ID!
    title: String
    subSection: String
    questions: [Question]


type SectionConnection 
    items: [Section]
    nextToken: String


type Subscription 
    onCreateSection(id: ID, title: String): Section
        @aws_subscribe(mutations: ["createSection"])
    onUpdateSection(id: ID, title: String): Section
        @aws_subscribe(mutations: ["updateSection"])
    onDeleteSection(id: ID, title: String): Section
        @aws_subscribe(mutations: ["deleteSection"])
    onCreateScore(
        id: ID,
        score: Int,
        questionId: ID,
        userId: ID
    ): Score
        @aws_subscribe(mutations: ["createScore"])
    onUpdateScore(
        id: ID,
        score: Int,
        questionId: ID,
        userId: ID
    ): Score
        @aws_subscribe(mutations: ["updateScore"])
    onDeleteScore(
        id: ID,
        score: Int,
        questionId: ID,
        userId: ID
    ): Score
        @aws_subscribe(mutations: ["deleteScore"])
    onCreateQuestion(id: ID, text: String, sectionId: ID): Question
        @aws_subscribe(mutations: ["createQuestion"])
    onUpdateQuestion(id: ID, text: String, sectionId: ID): Question
        @aws_subscribe(mutations: ["updateQuestion"])
    onDeleteQuestion(id: ID, text: String, sectionId: ID): Question
        @aws_subscribe(mutations: ["deleteQuestion"])
    onCreateUser(
        id: ID,
        email: String,
        jobTitle: String,
        jobTitleShare: Boolean,
        department: String
    ): User
        @aws_subscribe(mutations: ["createUser"])
    onUpdateUser(
        id: ID,
        email: String,
        jobTitle: String,
        jobTitleShare: Boolean,
        department: String
    ): User
        @aws_subscribe(mutations: ["updateUser"])
    onDeleteUser(
        id: ID,
        email: String,
        jobTitle: String,
        jobTitleShare: Boolean,
        department: String
    ): User
        @aws_subscribe(mutations: ["deleteUser"])


input TableBooleanFilterInput 
    ne: Boolean
    eq: Boolean


input TableFloatFilterInput 
    ne: Float
    eq: Float
    le: Float
    lt: Float
    ge: Float
    gt: Float
    contains: Float
    notContains: Float
    between: [Float]


input TableIDFilterInput 
    ne: ID
    eq: ID
    le: ID
    lt: ID
    ge: ID
    gt: ID
    contains: ID
    notContains: ID
    between: [ID]
    beginsWith: ID


input TableIntFilterInput 
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
    contains: Int
    notContains: Int
    between: [Int]


input TableQuestionFilterInput 
    id: TableIDFilterInput
    text: TableStringFilterInput
    sectionId: TableIDFilterInput


input TableScoreFilterInput 
    id: TableIDFilterInput
    score: TableIntFilterInput
    questionId: TableIDFilterInput
    userId: TableIDFilterInput


input TableSectionFilterInput 
    id: TableIDFilterInput
    title: TableStringFilterInput


input TableStringFilterInput 
    ne: String
    eq: String
    le: String
    lt: String
    ge: String
    gt: String
    contains: String
    notContains: String
    between: [String]
    beginsWith: String


input TableUserFilterInput 
    id: TableIDFilterInput
    email: TableStringFilterInput
    jobTitle: TableStringFilterInput
    jobTitleShare: TableBooleanFilterInput
    department: TableStringFilterInput
    level: TableIntFilterInput
    yearRange: TableIntFilterInput
    industry: TableStringFilterInput
    orgSize: TableIntFilterInput


input UpdateQuestionInput 
    id: ID!
    text: String
    sectionId: ID


input UpdateScoreInput 
    id: ID!
    score: Int
    questionId: ID
    userId: ID


input UpdateSectionInput 
    id: ID!
    title: String


input UpdateUserInput 
    id: ID!
    email: String
    jobTitle: String
    jobTitleShare: Boolean
    department: String
    level: Int
    yearRange: Int
    industry: String
    orgSize: Int


type User 
    id: ID!
    email: String
    jobTitle: String
    jobTitleShare: Boolean
    department: String
    level: Int
    yearRange: Int
    industry: String
    orgSize: Int


type UserConnection 
    items: [User]
    nextToken: String

请求在 AppSync 中有效:

【问题讨论】:

【参考方案1】:

由于数据已正确存储在 DynamoDB 中,看起来响应映射模板中可能存在类型不匹配。请使用 ALL 作为设置为您的 API 启用 Cloudwatch 日志记录。然后,您可以检查 CloudWatch 日志以获取评估的响应映射模板,然后将 ctx.result.data 与用户类型的形状进行比较。

如果仍然无法正常工作,请在此处发布您的架构和映射模板,以便我可以尝试复制它。谢谢。

【讨论】:

谢谢。由于完全相同的查询/映射模板在 AppSync 控制台中工作,我怀疑客户端正在发生其他事情。您能否为您的 API 启用 CloudWatch logs ALL 设置,然后尝试从 ReactJS 客户端运行此查询。完成此操作后,从响应标头中获取 requestId,并查看 CloudWatch 日志。请从日志中发布生成的 ResponseMapping 模板,以便我们检查响应映射中上下文的形状。 你找到解释了吗?我正在一个 Angular 应用程序中试验同样的事情。

以上是关于Graphql apollo 客户端从 AppSync 返回空值的主要内容,如果未能解决你的问题,请参考以下文章

angular 的 Apollo 客户端:从 graphql 文件生成代码

GraphQL + Apollo 请求

如何使用 apollo 客户端从 schema.graphql 中提取 typescript 接口:codegen

GraphQL/Apollo 客户端:如果查询是先前查询的子集,则从缓存中获取数据

angular, apollo-client, graphQL - 从 graphql 查询中选择所有字段

如何从 graphql apollo 客户端查询中获取/记录/捕获错误