FROM CACHE - en_header

Shopify App React and NodeJS tutorial - API_KEY not defined

tobrien
Tourist
5 1 1

Hi,

I am new to Shopify development and React (although I have developed for NodeJS Express before).

Following the steps in the 'Build a Shopify App with Node and React tutorial' (https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react/build-your-user-interface-with...), I have hit a snag.

At the point of setting the Shopify App Bridge, the instructions say to add a <Provider config={config}> tag. Having defined config previously as: { apiKey: API_KEY, shopOrigin: Cookies.get("shopOrigin"), forceRedirect: true }

The problem is, as I can see in the example code provided in the tutorial, API_KEY is not defined, and predictably I get the appropriate error when testing the app from the Store admin. Even if I define API_KEY as the SHOPIFY_API_KEY from process.env, I encounter a new error: apiKey must be provided.

I'm officially at a loss. Any help would be appreciated.

Capture_1.PNG

 

Replies 14 (14)
paulomarg
Shopify Staff
Shopify Staff
8 1 2

Hi @tobrien,

 

The API_KEY value here is expected to be set as an environment variable within your app. One way of doing that is to set the value as a webpack plugin (at compile time) in your next.config.js file like so:

const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY);

module.exports = withCSS({
  webpack: config => {
    const env = { API_KEY: apiKey };
    config.plugins.push(new webpack.DefinePlugin(env));
    return config;
  }
});

 

You can find some more information on DefinePlugin in the webpack docs.

 

Hope this helps!

Paulo

tobrien
Tourist
5 1 1

Hi @paulomarg 

Thank you for your response. I did forget to mention I have included this in my next.config.js:

require("dotenv").config()

const withCSS = require('@zeit/next-css')
const webpack = require('webpack')

const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY)

module.exports = withCSS({
  webpack: (config) => {
    const env = { API_KEY: apiKey }
    config.plugins.push(new webpack.DefinePlugin(env))
    return config
  }
})
Am I missing something obvious (like a typo)? Is it something to do with how I am starting the application or the structure of my project?
I did have everything working as in the tutorial up until this point.
 
Thanks
Tom
paulomarg
Shopify Staff
Shopify Staff
8 1 2

Hi again!

 

This looks OK to me, the demo project is set up so API_KEY should be set at that point. It could indeed be some other issue as you said.

 

The first thing I'd suggest is to make sure that your API key is set in your .env file, and to check that the value is being set at this point when you recompile your project.

tobrien
Tourist
5 1 1

For anyone else that encounters this, a restart of ngrok seemed to do the trick. Although I'm sure I had done that before posting, this also resolved the issue when I subsequently encountered the error.

Of course first check that next.config.js is set up properly as described.

samsuzuki
Tourist
4 0 1

I have the same issue 'AppBridgeError: APP::ERROR::INVALID_CONFIG: apiKey must be provided'.

I cannot solve this, by restarting ngrok, nodejs-server. 

Any solutions?

paulomarg
Shopify Staff
Shopify Staff
8 1 2

Hi @samsuzuki,

Can you please provide a bit more detail on the error you're seeing, like when does it happen, and the code that triggers it?

Also, just to check all the boxes, make sure your API key is set in your .env file and is being properly loaded when your server starts.

Thank you!

samsuzuki
Tourist
4 0 1

@paulomarg Thank you for your reply. Sorry for my lack of information.

I'm new as a React/ Node.js developer( usually I am a iOS app developer).

And I was doing this tutorial . It works fine halfway.

But, at this part(#Add Shopify Bridge), my code does not work fine. Before 'Add Shopify Bridge' section, my code works fine.

When I open below URL(my ngrok with OAuth) after writing "Add Shopify Bridge" code with restarting web-server and ngrok, this error message appear.

https://24e3f0c517ea.ngrok.io/auth?shop=xn-ccka2ewcwdtfo276cihcb50i.myshopify.com

Error Message:  

Unhandled Runtime Error

AppBridgeError: APP::ERROR::INVALID_CONFIG: apiKey must be provided

Browser(Google Chrome) console error message:

AppbridgeError_googlechrome_console.png

My development enviroment

  • Node.js: v10.22.0
  • npm: 6.14.6
  • macOS: 10.15.5 (Catalina)
  • Google Chrome: 84.0.4147.105
  • Project Package(package.json) dependencies
"@shopify/app-bridge-react": "^1.26.2",
"@shopify/koa-shopify-auth": "^3.1.65",
"@shopify/polaris": "^5.2.0",
"@zeit/next-css": "^1.0.1",
"dotenv": "^8.2.0",
"isomorphic-fetch": "^2.2.1",
"js-cookie": "^2.2.1",
"koa": "^2.13.0",
"koa-session": "^6.0.0",
"next": "^9.5.2",
"react": "^16.13.1",
"react-dom": "^16.13.1"
 

My code is below.

/.env 

SHOPIFY_API_KEY='xxxxxxxx'
SHOPIFY_API_SECRET_KEY='xxxxxxx'

/server.js

 

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 session = require('koa-session');

dotenv.config();

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 } = process.env;

app.prepare().then(() => {
    const server = new Koa();
    server.use(session({ secure: true, sameSite: 'none' }, server));
    server.keys = [SHOPIFY_API_SECRET_KEY];

    server.use(
        createShopifyAuth({
            apiKey: SHOPIFY_API_KEY,
            secret: SHOPIFY_API_SECRET_KEY,
            scopes: ['read_products'],
            afterAuth(ctx) {
                const { shop, accessToken} = ctx.session;
                ctx.cookies.set('shopOrigin', shop, {
                    httpOnly: false,
                    secure: true,
                    sameSite: 'none'
                  });
                ctx.redirect('/');
            },
        }),
    );

    server.use(verifyRequest());
    server.use(async (ctx) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
        ctx.res.statusCode = 200;
        return
    });

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

});

 

/page/_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 Cookies from 'js-cookie';

class MyApp extends App {
    render() {
        const { Component, pageProps } = this.props;
        const config = { apiKey: API_KEY, shopOrigin: Cookies.get("shopOrigin"), 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>
        );
    }
}

export default MyApp;

 

/page/index.js

 

import { EmptyState, Layout, Page, TextStyle } from '@shopify/polaris';
import { TitleBar } from '@shopify/app-bridge-react';

const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';

const Index = () => (
    <Page>
        <TitleBar
        title="Sample App"
        primaryAction={{
            content: 'Select products',
        }}
        />
        <Layout>
            <EmptyState
                heading="Discount your products temporarily"
                action={{
                    content: 'Select products',
                    onAction: () => console.log('clicked!'),
                }}
                image={img}
                >
                <p>Select products to change their price temporarily.</p>
            </EmptyState>
        </Layout>
    </Page>
);

export default Index;

 

 Other code: same as codes before 'Add Shopify Bridge' section

Thank you for your support.

samsuzuki
Tourist
4 0 1

@paulomarg  Also, "/next.config.js" is here. 

require("dotenv").config();
const withCSS = require('@zeit/next-css');
const webpack = require('webpack');

const apiKey = JSON.stringify(process.env.SHOPFIY_API_KEY);

module.exports = withCSS({
    webpack: (config) => {
        const env = { API_KEY: apiKey};
        config.plugins.push(new webpack.DefinePlugin(env));
        return config;
    },
});

  Thank you.

samsuzuki
Tourist
4 0 1

@paulomarg I solved! Sorry for bothering you.

The reason of the error is here. 

const apiKey = JSON.stringify(process.env.SHOPFIY_API_KEY);

" SHOPFIY" is typo. 

ash0kz
New Member
1 0 0

Hello @paulomarg,

I am having this same problem. I am currently getting the API_KEY not defined error when trying to access my .io site. Screen Shot 2020-09-14 at 4.05.10 PM.png

 

The following is my server.js file, 

 

 

1 require('isomorphic-fetch');
  2 const dotenv = require('dotenv');
  3 const Koa = require('koa');
  4 const next = require('next');
  5 const { default: createShopifyAuth } = require('@shopify/koa-shopify-auth');
  6 const { verifyRequest } = require('@shopify/koa-shopify-auth');
  7 const session = require('koa-session');
  8 
  9 dotenv.config();
 10 const port = parseInt(process.env.PORT, 10) || 3000;
 11 const dev = process.env.NODE_ENV !== 'production';
 12 const app = next({ dev });
 13 const handle = app.getRequestHandler();
 14 
 15 const { SHOPIFY_API_SECRET_KEY, SHOPIFY_API_KEY } = process.env;
 16 
 17 app.prepare().then(() => {
 18   const server = new Koa();
 19   server.use(session({ secure: true, sameSite: 'none' }, server));
 20   server.keys = [SHOPIFY_API_SECRET_KEY];
 21 
 22   server.use(
 23     createShopifyAuth({
 24       apiKey: SHOPIFY_API_KEY,
 25       secret: SHOPIFY_API_SECRET_KEY,
 26       scopes: ['read_products'],
 27       afterAuth(ctx) {
 28         const { shop, accessToken } = ctx.session;
 29         ctx.cookies.set('shopOrigin', shop, { httpOnly:false, secure: true, sameSite: 'none'});
 30        ctx.redirect('/');
 31       },
 32     }),
 33   );
 34 
 35   server.use(verifyRequest());
 36   server.use(async (ctx) => {
 37     await handle(ctx.req, ctx.res);
 38     ctx.respond = false;
 39     ctx.res.statusCode = 200;
 40     return
 41   });
 42 
 43   server.listen(port, () => {
 44     console.log(`> Ready on http://localhost:${port}`);
 45   });
 46 });

 

 

 

my pages/_app.js file,

 

 

  1 import App from 'next/app';
  2 import Head from 'next/head';
  3 import { AppProvider } from '@shopify/polaris';
  4 import { Provider } from '@shopify/app-bridge-react';
  5 import '@shopify/polaris/dist/styles.css';
  6 import translations from '@shopify/polaris/locales/en.json';
  7 import Cookies from 'js-cookie';
  8 
  9 
 10 class MyApp extends App {
 11   render() {
 12     const { Component, pageProps } = this.props;
 13     const config = { apiKey: API_KEY, shopOrigin: Cookies.get("shopOrigin"), forceRedirect: true     };
 14     return (
 15       <React.Fragment>
 16         <Head>
 17           <title>Sample App</title>
 18           <meta charSet="utf-8" />
 19         </Head>
 20         <Provider config={config}>
 21           <AppProvider i18n={translations}>
 22             <Component {...pageProps} />
 23           </AppProvider>
 24         </Provider>
 25       </React.Fragment>
 26     );
 27   }
 28 }
 29 
 30 export default MyApp;
 31 
~                                

 

 

 

and my next.config.js file,

 

 

  1 require("dotenv").config();
  2 const withCSS = require('@zeit/next-css');
  3 const webpack = require('webpack');
  4 
  5 const apiKey =  JSON.stringify(process.env.SHOPIFY_API_KEY);
  6 
  7 module.exports = withCSS({
  8   webpack: (config) => {
  9     const env = { API_KEY: apiKey };
 10     config.plugins.push(new webpack.DefinePlugin(env));
 11     return config;
 12   },
 13 });
~                                                                                                   
~         

 

 

 

I am following the same tutorial. Before this error, the app was working perfectly. I'm unsure how to proceed. My .env file also includes my secret api key, and api key. While looking for an error in my code, I redid this step in the tutorial multiple times, so I'm assuming that it is one of these lines that are the issue. I've also taken tobrian's advice and restarted ngrok, but this did not solve my issue. 

If you have any suggestions please let me know. 

 

Thank you! 

Ashlyn

paulomarg
Shopify Staff
Shopify Staff
8 1 2

Hi @ash0kz,

Your code seems to be right to me. One thing to keep in mind is that whenever you make changes to server.js and next.config.js, you need to actually stop and restart your server with NPM (step 5 of the App Bridge tutorial section), otherwise the changes won't take effect. Did you make sure to restart it after adding the lines?

To be sure that this code is being run, you can call console.log(apiKey); in next.config.js where you do

const env = { API_KEY: apiKey };

, and your API key should be printed in your terminal when you restart the server.

 

Hope this helps!

Jayvin
Shopify Partner
284 42 85
metasimian
New Member
1 0 1

Dear all,
Piggybacking on this thread as it is the same tutorial, same error, slight twist on the installation...   repl.it installing direct from github (vs. a local git clone, npm install + ngrok). 

The steps are as follows: 

  • log into repl.it & import https://github.com/Shopify/shopify-demo-app-node-react directly via https://repl.it/site/github 
  • add .env, process.env
    SHOPIFY_API_KEY=[YOUR_SHOPIFY_API_KEY_HERE]
    SHOPIFY_API_SECRET_KEY=[YOUR_SHOPIFY_API_SECRET_KEY_HERE]
    HOST=https://yourhosthere.repl.co
  • Setup Shopify
  • Compile & Run it in repl.it
    NOTE that one workaround, rather than using 'npm run dev' as per the tutorial I needed to run 'npm run build' in the console, then setup the Run button to use 'npm run start'. 
    This is due to two reasons:
    • npm run dev by itself threw me an error at runtime ("exit status 137  - Failed to exec dev script" repl.it seems to run out of memory)
    • the Run button uses the user context and so will take into account .env  
      if run from the console, .env appears to be ignored/not visible ("TypeError [ERR_INVALID_ARG_TYPE]: .... Received undefined")

Once everything was configured & up and running, the tutorial application goes through the installation, "payment", and register the callback, all nicely as expected. Adding a product also seems to trigger the callback as expected.   

But once the tutorial app is installed on a store, when Shopify tries to display the app dashboard itself ("Discount your products temporarily"), the app page content itself hangs. 

Chrome introspect shows the error "AppBridgeError apiKey must be provided".  With a bit of poking around, it seems that API_KEY is not picked up (nor shopOrigin) in _app.js .... 

I'm not sure if it is a repl.it or a React issue... client or server side (but probably not a Shopify one).  

Any ideas? .... 

Thx. 

W

ps: I did get it running on a local + ngrok... that said, the repl.it approach is much nicer for eventually collaborating and expanding on the original tutorial.... 

ertugrulhadzhao
New Member
2 0 0

i got the definetly same issue, when i tried to use heroku server rather than ngrok. If i use ngrok everything works fine.