A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
I am building a nodejs react Shopify app.
On my index.js I have a mutation that works fine when I give a specific hardcode id. Now I'm trying to get that product id by query in order to pass it to the mutation, but that doesn't return any data.
Here is my code:
import { TitleBar } from '@shopify/app-bridge-react';
import {
Banner,
DisplayText,
EmptyState,
Frame,
Heading,
Layout,
Page,
PageActions,
TextField,
TextStyle,
Toast,
Card,
ResourceList,
} from '@shopify/polaris';
import { Context } from '@shopify/app-bridge-react';
import gql from 'graphql-tag';
import React from 'react';
import { Query } from 'react-apollo';
import { Mutation } from 'react-apollo';
const UPDATE_PRICE = gql`
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
}
userErrors {
field
message
}
}
}
`;
const GET_PRODUCT = gql`
query products {
productByHandle(handle:"product-handle") {
id
}
}`;
var productId = "";
class Index extends React.Component {
state = {
showToast: false,
};
render() {
return (
<Frame>
<Page>
<Layout>
//This is not returning data
<Query query={GET_PRODUCT }>
{({data, loading, error}) => {
if(error){
console.log("Error: " + error);
}
//This is always true!
if(loading){
console.log("Loading...");
}
if (data){
console.log(data.productByHandle.id);
}
return (
<div>
</div>
);
}}
</Query>
//This works fine
<Mutation
mutation={UPDATE_PRICE}>
{(handleSubmit, {error, data}) => {
const showError = error && (
<Banner status="critical">{error.message}</Banner>
);
const showToast = data && (
<Toast
content="Sucessfully updated"
onDismiss={() => this.setState({ showToast: false })}
/>
);
return (
<Page>
<Layout>
{showToast}
<Layout.Section>
{showError}
</Layout.Section>
<PageActions
primaryAction={[
{
content: 'Turn on',
onAction: () => {
const productInput = {
id: "gid://shopify/Product/6604579405978",
status: "ACTIVE",
};
;
handleSubmit({
variables: { input: productInput },
});
}
}
]}
/>
<PageActions
primaryAction={[
{
content: 'Turn off',
onAction: () => {
const productInput = {
id: "gid://shopify/Product/6604579405978",
status: "DRAFT",
};
handleSubmit({
variables: { input: productInput },
});
}
}
]}
/>
</Layout>
</Page>
);
}}
</Mutation>
</Page>
</Frame>
);
}
}
export default Index;
This is my _app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
function MyProvider(props) {
const app = useAppBridge();
const client = new ApolloClient({
fetch: authenticatedFetch(app),
fetchOptions: {
credentials: "include",
},
});
const Component = props.Component;
return (
<ApolloProvider client={client}>
<Component {...props} />
</ApolloProvider>
);
}
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
shopOrigin: shopOrigin,
forceRedirect: true,
}}
>
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
Anyone has an idea why data is always undefined?
Solved! Go to the solution
This is an accepted solution.
Hey,
I did. I went through several changes on my index.js already so can't exactly recall what I changed, but here is my index.js & _app.js and hopefully helps you you figure it out.
Index.js
import { TitleBar } from "@shopify/app-bridge-react";
import {
Banner,
EmptyState,
Frame,
Heading,
Layout,
Page,
PageActions,
TextField,
TextStyle,
Toast,
Card,
ResourceList,
} from "@shopify/polaris";
import { Context } from "@shopify/app-bridge-react";
import gql from "graphql-tag";
import React from "react";
import { Query } from "react-apollo";
import { Mutation } from "react-apollo";
import { useQuery } from "@apollo/react-hooks";
const UPDATE_PRICE = gql`
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
}
userErrors {
field
message
}
}
}
`;
const GET_PRODUCT = gql`
query products {
productByHandle(handle: "your-handle") {
id
}
}
`;
var productID = "";
class Index extends React.Component {
state = {
showToast: false,
};
render() {
return (
<Frame>
<Page >
<Layout>
<Query query={GET_PRODUCT}>
{({ data, loading, error }) => {
if (loading) {
console.log("Loading....");
return <div></div>;
}
if (error) {
console.log("Error: ", error.message);
return <div></div>;
}
console.log("data: ", data.productByHandle.id);
productID = data.productByHandle.id;
return <div></div>;
}}
</Query>
<Mutation mutation={UPDATE_PRICE}>
{(handleSubmit, { error, data }) => {
const showError = error && (
<Banner status="critical">{error.message}</Banner>
);
const showToast = data && (
<Toast
content="Sucessfully updated"
onDismiss={() => this.setState({ showToast: false })}
/>
);
return (
<Page>
<Layout>
{showToast}
<Layout.Section>{showError}</Layout.Section>
<PageActions
primaryAction={[
{
content: "Turn on",
onAction: () => {
console.log("turn on");
const productInput = {
id: productID,
status: "ACTIVE",
};
handleSubmit({
variables: { input: productInput },
});
},
},
]}
/>
<PageActions
primaryAction={[
{
content: "Turn off",
onAction: () => {
console.log("turn off");
const productInput = {
id: productID,
status: "DRAFT",
};
handleSubmit({
variables: { input: productInput },
});
},
},
]}
/>
</Layout>
</Page>
);
}}
</Mutation>
</Layout>
</Page>
</Frame>
);
}
}
export default Index;
_app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import { Redirect } from "@shopify/app-bridge/actions";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
function userLoggedInFetch(app) {
const fetchFunction = authenticatedFetch(app);
return async (uri, options) => {
const response = await fetchFunction(uri, options);
if (response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1") {
const authUrlHeader = response.headers.get("X-Shopify-API-Request-Failure-Reauthorize-Url");
const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
return null;
}
return response;
};
}
function MyProvider(props) {
const app = useAppBridge();
const client = new ApolloClient({
fetch: userLoggedInFetch(app),
fetchOptions: {
credentials: "include",
},
});
const Component = props.Component;
return (
<ApolloProvider client={client}>
<Component {...props} />
</ApolloProvider>
);
}
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
shopOrigin: shopOrigin,
forceRedirect: true,
}}
>
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
Good luck!
Hi, do you solve this problem ?
This is an accepted solution.
Hey,
I did. I went through several changes on my index.js already so can't exactly recall what I changed, but here is my index.js & _app.js and hopefully helps you you figure it out.
Index.js
import { TitleBar } from "@shopify/app-bridge-react";
import {
Banner,
EmptyState,
Frame,
Heading,
Layout,
Page,
PageActions,
TextField,
TextStyle,
Toast,
Card,
ResourceList,
} from "@shopify/polaris";
import { Context } from "@shopify/app-bridge-react";
import gql from "graphql-tag";
import React from "react";
import { Query } from "react-apollo";
import { Mutation } from "react-apollo";
import { useQuery } from "@apollo/react-hooks";
const UPDATE_PRICE = gql`
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
}
userErrors {
field
message
}
}
}
`;
const GET_PRODUCT = gql`
query products {
productByHandle(handle: "your-handle") {
id
}
}
`;
var productID = "";
class Index extends React.Component {
state = {
showToast: false,
};
render() {
return (
<Frame>
<Page >
<Layout>
<Query query={GET_PRODUCT}>
{({ data, loading, error }) => {
if (loading) {
console.log("Loading....");
return <div></div>;
}
if (error) {
console.log("Error: ", error.message);
return <div></div>;
}
console.log("data: ", data.productByHandle.id);
productID = data.productByHandle.id;
return <div></div>;
}}
</Query>
<Mutation mutation={UPDATE_PRICE}>
{(handleSubmit, { error, data }) => {
const showError = error && (
<Banner status="critical">{error.message}</Banner>
);
const showToast = data && (
<Toast
content="Sucessfully updated"
onDismiss={() => this.setState({ showToast: false })}
/>
);
return (
<Page>
<Layout>
{showToast}
<Layout.Section>{showError}</Layout.Section>
<PageActions
primaryAction={[
{
content: "Turn on",
onAction: () => {
console.log("turn on");
const productInput = {
id: productID,
status: "ACTIVE",
};
handleSubmit({
variables: { input: productInput },
});
},
},
]}
/>
<PageActions
primaryAction={[
{
content: "Turn off",
onAction: () => {
console.log("turn off");
const productInput = {
id: productID,
status: "DRAFT",
};
handleSubmit({
variables: { input: productInput },
});
},
},
]}
/>
</Layout>
</Page>
);
}}
</Mutation>
</Layout>
</Page>
</Frame>
);
}
}
export default Index;
_app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import { Redirect } from "@shopify/app-bridge/actions";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
function userLoggedInFetch(app) {
const fetchFunction = authenticatedFetch(app);
return async (uri, options) => {
const response = await fetchFunction(uri, options);
if (response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1") {
const authUrlHeader = response.headers.get("X-Shopify-API-Request-Failure-Reauthorize-Url");
const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
return null;
}
return response;
};
}
function MyProvider(props) {
const app = useAppBridge();
const client = new ApolloClient({
fetch: userLoggedInFetch(app),
fetchOptions: {
credentials: "include",
},
});
const Component = props.Component;
return (
<ApolloProvider client={client}>
<Component {...props} />
</ApolloProvider>
);
}
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
shopOrigin: shopOrigin,
forceRedirect: true,
}}
>
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
Good luck!
Thanks a lot!!!