shopify-app-template-node
shopify-app-template-node copied to clipboard
AppBridgeError INVALID_CONFIG: host must be provided
When I open the app I get this error:
Unhandled Runtime Error
AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided
Sometimes I can't even install it because of this.
I have both .env and process.env files completed. Sometimes I see for a second a message popup to enable cookies but I can't press Enable, the redirect is too fast.
https://github.com/Shopify/shopify-app-bridge/issues/48
The same thing happened to me, I added it by adding the host
to the configuration.
A new query parameter named host
has been introduced with the release of App Bridge 2.0. The base64-encoded host parameter represents the domain that is currently hosting your embedded app.
We start with the env file, it is important that you have the SHOPIFY_APP_URL
defined, since it is the value that I use as host
.
SHOPIFY_API_KEY=123
SHOPIFY_API_SECRET=shpss_123
SHOPIFY_API_SCOPES=read_products,write_products,read_script_tags,write_script_tags
SHOPIFY_APP_URL=https://123.ngrok.io
Now, in your next.config.js
file you must take that value and send it, as well as the apiKey
.
// next.config.js
require("dotenv").config();
const webpack = require("webpack");
const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY);
const host = JSON.stringify(process.env.SHOPIFY_APP_URL);
module.exports = {
webpack: (config) => {
const env = { API_KEY: apiKey, HOST_URL: host };
config.plugins.push(new webpack.DefinePlugin(env));
return config;
},
};
Finally you have to pass that host
to the configuration. This must be modified with base64 and that's it. You can see your app from the store you are testing in.
import React from "react";
import App from "next/app";
import Head from "next/head";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/es.json";
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
const config = {
apiKey: API_KEY,
shopOrigin,
host: Buffer.from(HOST_URL).toString("base64"),
forceRedirect: true,
};
return (
<React.Fragment>
<Head>
<title>Sample App</title>
<meta charSet="utf-8" />
</Head>
<Provider config={config}>
<AppProvider i18n={translations}>
<Component {...pageProps} />
</AppProvider>
</Provider>
</React.Fragment>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
@FiliSantillan wait why are we pulling from env
when it's available in the query?
Update your pages/_app.js
to include host
in getInitialProps
and then call it in your config
from this.props
along side shopOrigin
const { Component, pageProps, shopOrigin, host } = this.props;
const config = { apiKey: API_KEY, shopOrigin, host, forceRedirect: true };
...
...
...
MyApp.getInitialProps = async ({ ctx }) => {
return { shopOrigin: ctx.query.shop, host: ctx.query.host };
};
And if you're using something like recurring subscriptions, you'll need to get host from the URL and pass it in your return URL
const getHost = (ctx) => {
const baseUrl = new URL(`https://${ctx.request.header.host}${ctx.request.url}`);
return baseUrl.searchParams.get("host");
};
server.use(
createShopifyAuth({ async afterAuth(ctx) {
const { shop, scope, accessToken } = ctx.state.shopify;
const host = getHost(ctx);
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const returnUrl = `https://${Shopify.Context.HOST_NAME}?host=${host}&shop=${shop}`;
const subscriptionUrl = await getSubscriptionUrl(accessToken, shop, returnUrl );
ctx.redirect(subscriptionUrl);
},
})
);
Did this help anyone? I have the same problem, still. server.js : from the node tutorial.
require("isomorphic-fetch");
const dotenv = require("dotenv");
const Koa = require("koa");
const next = require("next");
const { default: createShopifyAuth } = 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.April21,
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(
createShopifyAuth({
afterAuth(ctx) {
const { shop, scope } = ctx.state.shopify;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
ctx.redirect(`/`);
ctx.redirect(`/?shop=${shop}`);
},
})
);
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;
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}`);
});
});
.env
SHOPIFY_API_KEY=0000
SHOPIFY_API_SECRET=0000
SHOPIFY_API_SCOPES=read_products
SHOPIFY_APP_URL=https://daee3b7889af.ngrok.io
next.config.js
require("dotenv").config();
const webpack = require("webpack");
const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY);
const host = JSON.stringify(process.env.SHOPIFY_APP_URL);
module.exports = {
webpack: (config) => {
const env = { API_KEY: apiKey, HOST_URL: host };
config.plugins.push(new webpack.DefinePlugin(env));
return config;
},
};
app.js
import App from "next/app";
import Head from "next/head";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
import ClientRouter from "../components/ClientRouter";
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin, host } = this.props;
const config = {
apiKey: API_KEY,
shopOrigin,
host,
forceRedirect: true,
};
return (
<React.Fragment>
<Head>
<title>Sample App</title>
<meta charSet="utf-8" />
</Head>
<Provider config={config}>
<ClientRouter />
<AppProvider i18n={translations}>
<Component {...pageProps} />
</AppProvider>
</Provider>
</React.Fragment>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop, host: ctx.query.host
};
};
export default MyApp;
looks like this was fixed by #629
but the tutorial is not updated yet. Still had to add what @kinngh posted ( thanks!).
@lubojanski thanks for reminding that now you don't have to use a separate function to call in on host
and instead it's directly available. I've updated the code on my template repository available here or you can directly look at the individual files at pages/_app.js
and server.js
. Though I hope they kinda update the repo soon.
Anyone figure out a fix for this I'm trying to deploy the app to AWS Cloudfront and this issue keeps popping up. Man deploying shopify apps to AWS is a F***** nightmare this really needs worked out better. In 13 years of development I've never seen any company use Heroku everyone is on AWS/Azure.
Im still facing this issue. Seems my code is pretty correct but after moving to vercel it starts giving me this error......
when running locally and using local-tunnel/ngrok all works but after deploying to vercel and changing my URLs it stops working.......
@joshbedo I'd appriciate if you could share any insights you found? seems you were deploying to AWS.... did it work? did you change anything?
Any updates on this?
+1
Please note there is a bug in some versions of @shopify/app-bridge-react that will cause the "host must be provided error".
This bug randomly occurred after an "npm update" with no code changes.
app-bridge-react 2.0.24 seems to be the only stable version.
Also see https://community.shopify.com/c/shopify-apis-and-sdks/shopify-app-bridge-react-getting-error-host-not-provided-after/m-p/1579622
This also happens when the session expires after 24 hours. Had to manually redirect the app to the auth url to reauthenticate the user to fix this issue.
This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.
We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.
If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines
Thank you!
For Remix developers,
To resolve this issue, include the following code in the app.tsx file. This addition should fix the issue.
export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const params = new URLSearchParams(url.search);
const host = params.get('host');
await authenticate.admin(request);
return json({ apiKey: process.env.SHOPIFY_API_KEY || "", host: host });
};