FROM CACHE - en_header

Shopify App Bridge not working

Solved
nandezer
Tourist
12 2 8

Hey, 

 

My node Nodejs React public app is looping endlessly when opening it after installation. 

When I open the first app for the first time on the Heroku server it's all good and as long as the server does not idle it works well. If the Heroku server idles (Heroku) and I reopen the app, I get on an endless loop where router.get("/", async (ctx)) gets called but the redirect doesn't happen.

Here is my server.js

require("isomorphic-fetch");
const dotenv = require("dotenv");
const Koa = require("koa");
const next = require("next");
const { default: shopifyAuth } = require("@shopify/koa-shopify-auth");
const { verifyRequest } = require("@shopify/koa-shopify-auth");
const { default: Shopify, ApiVersion } = require("@shopify/shopify-api");
const Router = require("koa-router");

dotenv.config();

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SHOPIFY_API_SCOPES.split(","),
  HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/https:\/\//, ""),
  API_VERSION: ApiVersion.October20,
  IS_EMBEDDED_APP: true,
  SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

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

const ACTIVE_SHOPIFY_SHOPS = {};

app.prepare().then(() => {
  const server = new Koa();
  const router = new Router();
  server.keys = [Shopify.Context.API_SECRET_KEY];

  server.use(
    shopifyAuth({
      afterAuth(ctx) {
        // console.log(ctx.state.shopify.shop);
        const { shop, scope } = ctx.state.shopify;
        ACTIVE_SHOPIFY_SHOPS[shop] = scope;
        ctx.redirect(`/`);
      },
    })
  );

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

  router.get("/", async (ctx) => {
    const shop = ctx.query.shop;
    console.log(ctx.query.shop);
    if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
      ctx.redirect(`/auth?shop=${shop}`);
    } else {
      await handleRequest(ctx);
    }
  });
  router.get("(/_next/static/.*)", handleRequest);
  router.get("/_next/webpack-hmr", handleRequest);
  router.get("(.*)", verifyRequest(), handleRequest);

  server.use(router.allowedMethods());
  server.use(router.routes());

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

 

and 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 { 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, host } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            shopOrigin: shopOrigin,
            host: host,
            forceRedirect: true,
          }}
        >
          <MyProvider Component={Component} {...pageProps} />
        </Provider>
      </AppProvider>
    );
  }
}

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

export default MyApp;

 

 

Accepted Solution (1)

Accepted Solutions
ElanaK
Shopify Staff
Shopify Staff
13 2 1

This is an accepted solution.

Hi @nandezer ,

In your Provider config, could you try removing both the `shopOrigin` and the `forceRedirect` keys? The `shopOrigin` is no longer required to initialize the Provider (we're in the process of updating our docs right now) and the `forceRedirect` should just redirect back to Admin if it's not already there. That would be the only place that App Bridge is initiating a redirect and it would help us pinpoint what is causing the issue.

The Provider initialization should look like this:

 <Provider
    config={{
        apiKey: API_KEY,
        host: host,
    }}
>

 

Also, if you could share any of the errors you're running into, we can see if we can investigate further!

View solution in original post

Replies 2 (2)
ElanaK
Shopify Staff
Shopify Staff
13 2 1

This is an accepted solution.

Hi @nandezer ,

In your Provider config, could you try removing both the `shopOrigin` and the `forceRedirect` keys? The `shopOrigin` is no longer required to initialize the Provider (we're in the process of updating our docs right now) and the `forceRedirect` should just redirect back to Admin if it's not already there. That would be the only place that App Bridge is initiating a redirect and it would help us pinpoint what is causing the issue.

The Provider initialization should look like this:

 <Provider
    config={{
        apiKey: API_KEY,
        host: host,
    }}
>

 

Also, if you could share any of the errors you're running into, we can see if we can investigate further!

nandezer
Tourist
12 2 8

Hey @ElanaK

 

Thanks so much for the advice, that was it! I hadn't updated app-bridge packages to the latest version so it was working with shopOrigin, not with host.

After updating it on my server.js I had to modify the redirect link to:

 

ctx.redirect(`/?shop=${shop}&host=${host}`);

 

 

It works like a charm now!