1
votes

I'm brand new to Apollo and graphql, and I'm trying to setup an e-commerce site with shopify's storefront API. The website is build with react and Next.js for SSR.

I've managed to get some boilerplate code working for basic cart interactions with shopify. I have a Page component that wraps the whole app, and sits below ApolloProvider with access to apollo client. At the moment I'm using compose() to feed my Page component with some graphql (taken from this example):

const pageWithData = compose(
  graphql(query), // Query that retrieves base shopify information, such as shop name, description and products
  graphql(createCheckout, {name: "createCheckout"}), // Mutation that creates a new checkout object with shopify. Basically a cart object
  graphql(checkoutLineItemsAdd, {name: "checkoutLineItemsAdd"}), // Mutation that adds a new lineitem to the checkout object
  graphql(checkoutLineItemsUpdate, {name: "checkoutLineItemsUpdate"}), // Mutation that updates a line item
  graphql(checkoutLineItemsRemove, {name: "checkoutLineItemsRemove"}), // Mutation that removes a lineitem
)(Page);

This all works as expected, Except when i refresh the browser, the cart is emptied, and a new checkout object is created. So what I want to do is store the checkout ID in localStorage and check if there is an ID in localstorage, before creating a new checkout object. If there is one, i'll load that checkout instead. right now the checkout is created like so in the Page component:

componentWillMount() {
    this.props.createCheckout({
        variables: {
            input: {}
        }}).then((res) => {
        this.setState({
            checkout: res.data.checkoutCreate.checkout
        });
    });
}

Now, I've found a working graphql query to load an existing checkout based on an ID:

const checkoutFetchQuery = gql`
  query checkoutFetch ($checkoutId: ID!) {
      node(id: $checkoutId) {
        ... on Checkout {
          webUrl
          subtotalPrice
          totalTax
          totalPrice
          lineItems (first:250) {
            pageInfo {
              hasNextPage
              hasPreviousPage
            }
            edges {
              node {
                title
                variant {
                  title
                  image {
                    src
                  }
                  price
                }
                quantity
              }
            }
          }
        }
      }
    }
`;

And so I thought I could simply add this to the compose method like this:

const pageWithData = compose(
  graphql(query), // Query that retrieves base shopify information, such as shop name, description and products

  graphql(checkoutFetchQuery, { name: "fetchCheckout"}), // Query that fetches checkout based on a checkoutID

  graphql(createCheckout, {name: "createCheckout"}), // Mutation that creates a new checkout object with shopify. Basically a cart object
  graphql(checkoutLineItemsAdd, {name: "checkoutLineItemsAdd"}), // Mutation that adds a new lineitem to the checkout object
  graphql(checkoutLineItemsUpdate, {name: "checkoutLineItemsUpdate"}), // Mutation that updates a line item
  graphql(checkoutLineItemsRemove, {name: "checkoutLineItemsRemove"}), // Mutation that removes a lineitem
)(Page);

But this results in the following error from the Apollo dev tools:

GraphQL Errors: Variable checkoutId of type ID! was provided invalid value

I'm certain that this is me not understanding some key concept of how compose() work in react-apollo. I understand that I need to feed the query with some variables, but for some reason this query seem to run immediately on load, where as I expected this would simply make the query available on the component. Some of the other graphql() statements expects variables as well, such as "checkoutLineItemsAdd", but this doesn't result in errors. Another thing I've noticed is that the mutations are added as functions to the component props, where as my query is added as an object.

I'm struggling to find any good documentation on this.

  • Are queries run immediately?
  • Are mutations waiting to be called from the component, allowing us to dynamically add variables?
  • Should I write my gql syntax differently for it to become a function on the component instead of an object?
  • How do we pass variables dynamically to queries, when attached to the compose HOC?
  • Howcome I get errors from this query, and not the mutations, that also expect variables before running?
1

1 Answers

2
votes

Your query requires an input checkoutId which is of type ID.

But your query graphql(checkoutFetchQuery, { name: "fetchCheckout"}) is being fired without any input. You can add the input variables by doing so

graphql(checkoutFetchQuery, {
  name: "fetchCheckout",
  options: {
    variables: {
      checkoutId: localstorage.get('checkoutid')
    }
  }
})

The docs for options config is here

You can also skip a query from autofiring by adding a skip check under options like

options: {
  skip: !localstorage.get('checkoutid')
}