1
votes

I've set up a React application with React-Apollo and can successfully query my API.

However, when I make a mutation, a strange effect occurs. I get all the data back successfully (as seen in the Network tab of the Chrome Dev Tools), but when trying to console.log the data, it says that it is null.

Here's the relevant code:

// mutation file

import gql from "graphql-tag";
export default gql`
  mutation CreateReservation($name: String!, $time: String!, $persons: String, $specialMessage: String, $email: String!) {
    createReservation(input: {
        name: $name,
        time: $time,
        persons: $persons,
        specialMessage: $specialMessage,
        email: $email
    }) {
        __typename
        error
        data
    }
  }
`;

// component file

import React from "react";
import {graphql, compose} from "react-apollo";
import CreateReservationMutation from "../../graphql/mutations/api/CreateReservation";

class Reservations extends React.Component {
testGraphQL = ({ reservationName, reservationTime, reservationEmail, reservationPartySize, reservationMessage}) => {

    const variables = {
        name: reservationName,
        time: reservationTime,
        persons: reservationPartySize,
        specialMessage: reservationMessage,
        email: reservationEmail
    };

    this.props.createReservation(variables)
    .then(({data}) => {
        console.log("Data received: ", data); // Here is the problem
    }).catch((err) => {
        console.log("Error sending data: ", err);
    })
}
render() {
    return (
        <div>
            ...
            <div>
                <Form 
                    ...
                    submitFunc={this.testGraphQL}
                />
            </div>
            ...
            </div>
        </div>
    )
  }
}

export default compose(
    graphql(CreateReservationMutation, {
        props: ({mutate}) => ({
            createReservation: (variables) => mutate({variables})
        })
    })
)(Reservations);

So when I call the testGraphQL function, I receive the following in the console:

Data received:  {createReservation: null}

But when looking in the Network tab, I see that the data is actually there after all and is exactly what I am looking for. Furthermore, my database is correctly updated with all the reservation details, so I know with certainty that the mutation is being executed.

This is what I see from the Network tab:

{"data":{"createReservation":{"__typename":"ReservationResponse","error":null,"data":"Success"}}}

That is what I expect to see when I call console.log in testGraphQL.

So I know that I don't have any errors with my schema, my Apollo Client, or my mutation file.

Instead, the problem has to lie with how I'm setting up my compose statement or with how I'm calling the mutation itself.

Please let me know if you spot the error here. Thank you

UPDATE

I should mention that I am using AWS AppSync as my GraphQL provider.

The mutation calls a lambda function which does the following:

...
Promise.all([dynamodbPromise, snsPromise, sesPromise])
    .then((data) => {
        callback(null, {data: "Success"});
    })
    .catch((err) => {
        callback(null, {error: err});
    });

Here is my resolver for this mutation:

// request mapping template

{
  "version" : "2017-02-28",
  "operation": "Invoke",
  "payload": $util.toJson($ctx.args.input)
}

//  response mapping template

$util.toJson($context.result)

UPDATE 2

Configuring an optimisticResonse and rewriting my mutation like this:

this.props.createReservation({
        variables,
        optimisticResponse: {
            createReservation: {
                __typename: "ReservationResponse",
                errorMessage: null,
                responseMessage: "_TEST_"
            }
        }
    })
    .then(res => {
        console.log("Apllo Data: ", res);
    })

Leads me to get only the data from the optimistic response, which is this:

{data:
  createReservation: {
    __typename: "ReservationResponse", 
    errorMessage: null, 
    responseMessage: "Optimistic Success"
}

So the data returned must not be updated with the actual response from the API.

How can I now force Apollo to update the response after returning the optimistic response?

3

3 Answers

1
votes

You have two options to get the data response from a mutation:

In the Mutation component:

<Mutation>{mutation, {data}}</Mutation>

Or in the mutation function:

mutate().then(data => ...)

You are getting in the mutate promise response, but you are expecting the state object that apollo pass to the Mutation component.

It doesn't make sense for apollo to pass the state object, because if the mutation resolved it was successful, any error will make the promise to be rejected and no loading state is provided during the mutate call.

So to fix your code, you just need to change this

this.props.createReservation(variables)
    .then(data => {
0
votes

I finally solved it, though the solution is less than ideal.

I had to write an update function for my mutation. This is a barebones implementation that eventually gives me the actual data from the API:

this.props.createReservation({
        variables,
        optimisticResponse: {
            createReservation: {
                __typename: "ReservationResponse",
                errorMessage: null,
                responseMessage: "Optimistic Success"
            }
        },
        update: (proxy, {data: {createReservation}}) => {
            console.log("Update: ", createReservation);
        }
    })
    .then(res => {
        console.log("Apllo Data: ", res);
    })

The update function gets called three times. The first two fire before the promise is resolved and include the data from the optimisticResponse as shown in my "Update 2" section. The third call finally returns the actual data from the API.

While this will work for now, please let me know if there is a more direct method here. In this case, I don't want the optimisticResponse returned at all. I only want the actual data from my API to be returned. This way I can give my users proper error handling in the event something goes wrong.

Thank you, and I hope that we can help other people that have this issue.

0
votes

you can access the returned properties just by useing res.data

this.props.createReservation({
    variables,
    optimisticResponse: {
        createReservation: {
            __typename: "ReservationResponse",
            errorMessage: null,
            responseMessage: "_TEST_"
        }
    }
})
.then(res => {
    console.log("Apllo Data: ", res.data);
})