InternalServerError: Failed to make Shopify HTTP request: FetchError: invalid json response body

Solved

InternalServerError: Failed to make Shopify HTTP request: FetchError: invalid json response body

g3misa
Shopify Partner
24 5 15

Hi,

 

I am trying to create my first embedded admin app and everything seems to be working fine with API version

"2021-04".

 

However, when i switch to API version "2021-10", I get an INTERNAL SERVER ERROR on this request as my app tries to load:

 

https://cf93-47-155-234-31.ngrok.io/auth/callback?code=69a908234e220f32a5bddfe32c6b11f1&hmac=cdc672a2cb88c7213e191734fda314c6ffa919aa3a5a51255959ccc80e44514b&host=c29jaWFsLWNvbW1lcmNlLWRlbW8ubXlzaG9waWZ5LmNvbS9hZG1pbg&shop=g3-demo.myshopify.com&state=185784368861671&timestamp=1641087145

 

 

The Node error on my terminal is this:

 

InternalServerError: Failed to make Shopify HTTP request: FetchError: invalid json response body at https://g3-demo.myshopify.com/admin/api/undefined/graphql.json reason: Unexpected end of JSON input
┃       at Object.throw (/Users/gmisa/Dev/g3-demo/node_modules/koa/lib/context.js:97:11)
┃       at /Users/gmisa/Dev/g3-demo/node_modules/@shopify/koa-shopify-auth/dist/src/auth/index.js:102:42
┃       at step (/Users/gmisa/Dev/g3-demo/node_modules/tslib/tslib.js:133:27)
┃       at Object.throw (/Users/gmisa/Dev/g3-demo/node_modules/tslib/tslib.js:114:57)
┃       at rejected (/Users/gmisa/Dev/g3-demo/node_modules/tslib/tslib.js:105:69)
┃       at runMicrotasks (<anonymous>)
┃       at processTicksAndRejections (internal/process/task_queues.js:97:5)

 

 

Seems like there is "undefined" in the URL:

 

https://g3-demo.myshopify.com/admin/api/undefined/graphql.json

 

 

This only happens on API version "2021-10" and "2022-01".  Previous API versions work just fine.  Hope someone can help.  Thank you!

 

Here is my _app.js:

 

import { ApolloClient, HttpLink, ApolloProvider, InMemoryCache } from "@apollo/client";
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 { store, persistor } from '../modules/store';
import { Provider as ReduxProvider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { compose } from 'redux';
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 fetchOptions = {
    credentials: "include"
  };
  const link = new HttpLink({ fetch: userLoggedInFetch(app), fetchOptions });

  const client = new ApolloClient({
    cache: new InMemoryCache(),
    link
  });

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} />
    </ApolloProvider>
  );
}

class MyApp extends App {
  render() {
    const { Component, pageProps, host } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            host: host,
            forceRedirect: true,
          }}
        >
          <ReduxProvider store={store}>
            <PersistGate persistor={persistor}>
              <MyProvider Component={Component} {...pageProps} />
            </PersistGate>
          </ReduxProvider>
        </Provider>
      </AppProvider>
    );
  }
}

MyApp.getInitialProps = async ({ ctx }) => {
  return {
    host: ctx.query.host,
  };
};

export default MyApp;

 

 

Here's my server.js:

 

import "@babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "@shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";

dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
  dev,
});
const handle = app.getRequestHandler();

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SCOPES.split(","),
  HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
  API_VERSION: ApiVersion.October21,
  IS_EMBEDDED_APP: true,
  // This should be replaced with your preferred storage strategy
  SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};

app.prepare().then(async () => {
  const server = new Koa();
  const router = new Router();
  server.keys = [Shopify.Context.API_SECRET_KEY];
  server.use(
    createShopifyAuth({
      async afterAuth(ctx) {
        // Access token and shop available in ctx.state.shopify
        const { shop, accessToken, scope } = ctx.state.shopify;
        const host = ctx.query.host;
        ACTIVE_SHOPIFY_SHOPS[shop] = scope;

        const response = await Shopify.Webhooks.Registry.register({
          shop,
          accessToken,
          path: "/webhooks",
          topic: "APP_UNINSTALLED",
          webhookHandler: async (topic, shop, body) =>
            delete ACTIVE_SHOPIFY_SHOPS[shop],
        });

        if (!response.success) {
          console.log(
            `Failed to register APP_UNINSTALLED webhook: ${response.result}`
          );
        }

        // Redirect to app with shop parameter upon auth
        ctx.redirect(`/?shop=${shop}&host=${host}`);
      },
    })
  );

  const handleRequest = async (ctx) => {
    await handle(ctx.req, ctx.res);
    ctx.respond = false;
    ctx.res.statusCode = 200;
  };

  router.post("/webhooks", async (ctx) => {
    try {
      await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
      console.log(`Webhook processed, returned status code 200`);
    } catch (error) {
      console.log(`Failed to process webhook: ${error}`);
    }
  });

  router.post(
    "/graphql",
    verifyRequest({ returnHeader: true }),
    async (ctx, next) => {
      await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
    }
  );

  router.get("(/_next/static/.*)", handleRequest); // Static content is clear
  router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
  router.get("(.*)", async (ctx) => {
    const shop = ctx.query.shop;

    // This shop hasn't been seen yet, go through OAuth to create a session
    if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
      ctx.redirect(`/auth?shop=${shop}`);
    } else {
      await handleRequest(ctx);
    }
  });

  server.use(router.allowedMethods());
  server.use(router.routes());
  server.listen(port, () => {
    console.log(`> Ready on http://localhost:${port}`);
  });
});

 

 

Let's solve problems together!
Accepted Solution (1)

g3misa
Shopify Partner
24 5 15

This is an accepted solution.

Ok seems like I solved the issue.  In server.js, instead of using the API_VERSION enum, i used the string value '2021-10' and it worked.  

 

 

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SCOPES.split(","),
  HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
  API_VERSION: '2021-10',   //instead of using the API_VERSION enum, use the string value
  IS_EMBEDDED_APP: true,
  SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

 

 

Let's solve problems together!

View solution in original post

Replies 2 (2)

g3misa
Shopify Partner
24 5 15

This is an accepted solution.

Ok seems like I solved the issue.  In server.js, instead of using the API_VERSION enum, i used the string value '2021-10' and it worked.  

 

 

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SCOPES.split(","),
  HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
  API_VERSION: '2021-10',   //instead of using the API_VERSION enum, use the string value
  IS_EMBEDDED_APP: true,
  SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

 

 

Let's solve problems together!
devPrateek
Shopify Partner
4 0 1

This solved my issue, i just upgraded my shopify api version, and this issue suddenly occured, thanks you for the solution