A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
This is a GraphQL mystery! When testing my embedded app on a local environment everything works perfectly.
If I push to zeit and change my app details to the aliased domain - the app just responds with Network error: Unexpected token < in JSON at position 0. Attempting to install it from the partners dashboard also just ignores the entire charges screen if it's installed without Ngrok.
I'm following the embedded app tutorial with Node and React from Shopify.
.env SHOPIFY_API_KEY=000000 SHOPIFY_API_SECRET_KEY=000000 TUNNEL_URL='https://28a6d8fc.ngrok.io' // switching the tunnel URL to a domain breaks the app SERVER_URL='https://viaglamour.com' API_VERSION=2019-04
server.js require("isomorphic-fetch"); const Koa = require("koa"); const WPAPI = require("wpapi"); const next = require("next"); const { default: createShopifyAuth } = require("@shopify/koa-shopify-auth"); const dotenv = require("dotenv"); const { verifyRequest } = require("@shopify/koa-shopify-auth"); const session = require("koa-session"); const axios = require("axios"); dotenv.config(); const { default: graphQLProxy } = require("@shopify/koa-shopify-graphql-proxy"); const { ApiVersion } = require("@shopify/koa-shopify-graphql-proxy"); const Router = require("koa-router"); const { receiveWebhook, registerWebhook } = require("@shopify/koa-shopify-webhooks"); const processPayment = require("./server/router"); const port = parseInt(process.env.PORT, 10) || 3000; const dev = process.env.NODE_ENV !== "production"; const app = next({ dev }); const handle = app.getRequestHandler(); const { SHOPIFY_API_SECRET_KEY, SHOPIFY_API_KEY, TUNNEL_URL, SERVER_URL, API_VERSION } = process.env; app.prepare().then(() => { const server = new Koa(); const router = new Router(); server.use(session(server)); server.keys = [SHOPIFY_API_SECRET_KEY]; router.get("/", processPayment); server.use( createShopifyAuth({ apiKey: SHOPIFY_API_KEY, secret: SHOPIFY_API_SECRET_KEY, scopes: [ "read_products", "write_products", "read_orders", "read_fulfillments", "write_fulfillments", "write_shipping", "write_orders", "write_inventory" ], async afterAuth(ctx) { const { shop, accessToken } = ctx.session; const username = shop.replace(".myshopify.com", ""); ctx.cookies.set("shopOrigin", shop, { httpOnly: false }); ctx.cookies.set("token", accessToken, { httpOnly: false }); ctx.cookies.set("username", username, { httpOnly: false }); const stringifiedBillingParams = JSON.stringify({ recurring_application_charge: { name: "Cosmetic Formulations", price: 40, return_url: TUNNEL_URL, test: true, trial_days: 14, capped_amount: 40, terms: "" } }); const options = { method: "POST", body: stringifiedBillingParams, credentials: "include", headers: { "X-Shopify-Access-Token": accessToken, "Content-Type": "application/json" } }; const confirmationURL = await fetch( `https://${shop}/admin/api/${API_VERSION}/recurring_application_charges.json`, options ) .then(response => response.json()) .then( jsonData => jsonData.recurring_application_charge.confirmation_url ) .catch(error => console.log("error", error)); ctx.redirect(confirmationURL); } }) ); const webhook = receiveWebhook({ secret: SHOPIFY_API_SECRET_KEY }); router.post("/webhooks/products/create", webhook, ctx => { console.log("received webhook: ", ctx.state.webhook); console.log("webhook: ", webhook); }); server.use(graphQLProxy({ version: ApiVersion.April19 })); router.get("*", verifyRequest(), async ctx => { await handle(ctx.req, ctx.res); ctx.respond = false; ctx.res.statusCode = 200; }); server.use(router.allowedMethods()); server.use(router.routes()); server.listen(port, () => { console.log(`> Ready on http://localhost:${port}`); }); });
app.js import App from "next/app"; import Head from "next/head"; import { AppProvider } from "@shopify/polaris"; import "@shopify/polaris/styles.css"; import Cookies from "js-cookie"; import ApolloClient from "apollo-boost"; import { ApolloProvider } from "react-apollo"; import { HttpLink } from "apollo-link-http"; import { InMemoryCache } from "apollo-cache-inmemory"; import fetch from "isomorphic-fetch"; require("node-fetch"); const client = new ApolloClient({ fetchOptions: { credentials: "include" } }); // Polyfill fetch() on the server (used by apollo-client) if (!process.browser) { global.fetch = fetch; } class MyApp extends App { state = { shopOrigin: Cookies.get("shopOrigin") }; render() { console.log("shopOrigin", this.state.shopOrigin); const { Component, pageProps } = this.props; return ( <React.Fragment> <Head> <title>viaGlamour Ltd.</title> <meta charSet="utf-8" /> <script src="https://js.stripe.com/v3/" /> </Head> <AppProvider shopOrigin={this.state.shopOrigin} apiKey={API_KEY} forceRedirect > <ApolloProvider client={client}> <Component {...pageProps} /> </ApolloProvider> </AppProvider> </React.Fragment> ); } } export default MyApp;
.component (that does graphQL) import gql from "graphql-tag"; import { Query } from "react-apollo"; import { Card, ResourceList, Stack, Thumbnail, DisplayText, SkeletonBodyText, TextContainer, Layout, SkeletonPage, SkeletonDisplayText, Page, EmptyState, FooterHelp, Link, VisuallyHidden, Banner, Pagination, Button, TextStyle, InlineError } from "@shopify/polaris"; import store from "store-js"; import { Redirect } from "@shopify/app-bridge/actions"; import * as PropTypes from "prop-types"; import { getPrice, getRecommendedPrice } from "../common/prices.js"; import API from "../common/api.js"; import CustomerListItem from "../components/CustomerListItem"; import BasicListItem from "../components/BasicListItem"; import IndexPagination from "../components/IndexPagination"; import { getUsername, formatProductName } from "../common/helpers"; import Cookies from "js-cookie"; import axios from "axios"; const GET_PRODUCTS = gql` query Dog( $query: String! $after: String $before: String $first: Int $last: Int ) { products( first: $first last: $last after: $after before: $before query: $query ) { edges { cursor node { id title featuredImage { originalSrc } metafields(first: 6, namespace: "viaglamour") { edges { node { key value } } } variants(first: 1) { edges { node { id price inventoryItem { id } fulfillmentService { handle } } } } } } } } `; class ResourceListWithProducts extends React.Component { state = { item: "", data: "", searchValue: "", credits_required: 0, pageIndex: 0, afterCursor: null, beforeCursor: null, resultsPerPage: 10, first: 10, last: null, query: "vendor:viaglamour", sortValue: "DATE_MODIFIED_DESC" }; render() { return ( <Query query={GET_PRODUCTS} variables={{ after: this.state.afterCursor, before: this.state.beforeCursor, first: this.state.first, last: this.state.last, query: this.state.query //vendor:viaglamour AND product_type:lipstick }} > {({ data, loading, error }) => { if ( loading && (this.state.pageIndex > 0 || this.state.query != "vendor:viaglamour") ) { <Page title="Available Products"> <DisplayText size="small"> Browse products from your makeup line. </DisplayText>{" "} <br />{" "} <Layout> <Layout.Section> <Card sectioned> <SkeletonBodyText /> </Card> <Card sectioned> <TextContainer> <SkeletonDisplayText size="small" /> <SkeletonBodyText /> </TextContainer> </Card> </Layout.Section> </Layout> </Page>; } else if (loading) return ( <SkeletonPage primaryAction secondaryActions={2}> <Layout> <Layout.Section> <Card sectioned> <SkeletonBodyText /> </Card> <Card sectioned> <TextContainer> <SkeletonDisplayText size="small" /> <SkeletonBodyText /> </TextContainer> </Card> </Layout.Section> </Layout> </SkeletonPage> ); if (error) return <div>{error.message}</div>; return ( <Page title="Available Products"> <DisplayText size="small"> Browse products from your makeup line. </DisplayText> </Page> ); }} </Query> ); } } export default ResourceListWithProducts;
Stack Overflow suggests the error is happening because GraphQL is returning HTML because of an incorrect endpoint. But I don't understand what breaks the app just by switching the tunnel URL to a real domain.
Any hints would really help! So close to publishing!
Solved! Go to the solution
This is an accepted solution.
Sorry for not closing this thread! The issue got solved on Github.
For anyone else in the future - https://github.com/Shopify/shopify-demo-app-node-react/issues/32
I have a hunch, that may or may not be right, but is worth trying. Can you try changing
"Content-Type": "application/json"
to
"Content-Type": "application/graphql"
for post requests when you are running it on ZEIT? I believe the way you are sending your GraphQL bodies it's being interpreted as a string instead of graphql, and this can help if thats the case. Not sure 100% why it would work with ngrok and not on ZEIT but could be config somewhere also.
It also could not be the problem 🙂
Cheers!
Ryan
Ryan | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Thanks for replying! I tried introducing that header response - but no changes.
I noticed that when using my domain name the cookies are never set.
The domain can't authenticate, even though it's still using the same authentication library from Shopify! https://www.npmjs.com/package/@shopify/koa-shopify-auth
The /auth URL request just doesn't get recognized when using the real domain.
GraphQL might not be working because there are no cookies to authenticate the request.
GIF demo: https://gyazo.com/78e3a16fef817e73551895670eebaa5a
Then, maybe the real problem is that the KOA server doesn't understand /auth on my domain?
Is there any possible way to have the apps team look at this? I'm so close, yet so far away haha!
On my own domain it just shows a 404 not found.
Hey @keiraarts ,
This is a tricky one because our logs don't have any information available to indicate where the problem could be, this looks like a scenario where the debugging will have to be done on your end. I found a few articles that should provide some threads you can pull that'll hopefully help you uncover what is happening though :
https://github.com/graphql-boilerplates/node-graphql-server/issues/274#issuecomment-506995478
https://github.com/apollographql/apollo-feature-requests/issues/153
https://daveceddia.com/unexpected-token-in-json-at-position-0/
Josh | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
This is an accepted solution.
Sorry for not closing this thread! The issue got solved on Github.
For anyone else in the future - https://github.com/Shopify/shopify-demo-app-node-react/issues/32
there are no issues on that repository anymore, can you please share the solution? thanks!
Would you mind sharing the solution for the same. Thank you.