4
votes

I have an AppSync api set-up, with a specific mutation set-up. Which works fine when I test that mutation in AppSync. However when I try to use the same query in a react app, I'm getting a null value. However when I look in the network tab in my browser, the correct data is being returned from AppSync.

I have the following mutation in my React app:

import gql from 'graphql-tag';

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

My React component (slightly redacted for brevity):

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

When I inspect the query in the network tab, I see: {"data":{"createUser":{"__typename":"User","id":"860b7cec-e882-4242-aca0-d4865154b640"}}}

However, in my React component, I see: { "data": { "createUser": "null" } }

I'm not sure if I'm missing something around how the component is set-up with Apollo which means the data isn't being loaded proprely. But the query itself seems to work fine.

The data is also correctly stored in DynamoDB as expected.

Here is my request mapping:

{
  "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",
    },
  },
}

My response mapping:

$util.toJson($ctx.result)

And finally my schema:

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
}

The request works in AppSync:

enter image description here

1

1 Answers

0
votes

Since the data is correctly stored in DynamoDB, it looks like there could be a type mismatch in the Response Mapping template. Please enable Cloudwatch logging for your API with ALL as the setting. You can then check the CloudWatch logs for the evaluated Response Mapping template and then compare ctx.result.data with the shape of the User type.

If it still doesn't work, please post your schema and mapping templates here so I can try and replicate it on my end. Thanks.