Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Not able to pass the accessToken in header for API call (Public app for development store)

Not able to pass the accessToken in header for API call (Public app for development store)

shopquist
Visitor
1 0 1

Hi all,

So, I'm totally new to developing Shopify Apps and I followed the YouTube tutorial using the Shopify CLI tool (https://www.youtube.com/watch?v=PIXN032XJJ8).

The feature I'm trying to build now is that:

  1. A button is pressed in the embedded app
  2. A call is sent to the shopify API
  3. I want to print out the response (in this case, some list of products)

As of now, my index.js where I try to use a fetch looks like this:

import { Page, Form, FormLayout, Checkbox, TextField, Button } from "@shopify/polaris";
import { ResourcePicker } from "@shopify/app-bridge-react";
import React, {useState, useCallback} from "react";




export const Index = () => { 
//class Index extends React.Component {
  
      const getCollections = () => {
        var fetchUrl = "/api/products";
        var method = "GET";
        fetch(fetchUrl, { method: method })
        .then(response => response.json)
        .then(json => console.log(json))
        .catch( e => {
          console.log(e);
        })
      }  

      const [newsletter, setNewsletter] = useState(false);
      const [email, setEmail] = useState('');
    
      const handleSubmit = useCallback((_event) => {
        console.log('Reminder set for: ', email);
        getCollections();
        setEmail('');
        setNewsletter(false);
      }, [email]);
    
      const handleNewsLetterChange = useCallback((value) => { 
        
        setNewsletter(value);
        },[],
      );

      const handleEmailChange = useCallback((value) => setEmail(value), []);
    

    return (
    <Page
      title='You can get reminded about this product'
     
      
      <Form onSubmit={handleSubmit}>
        <FormLayout>
          <Checkbox
            label="Remind me about this product this weekend"
            checked={newsletter}
            onChange={handleNewsLetterChange}
          />
          <Button submit>Submit</Button> 
        </FormLayout>
      </Form>
    </Page>
    )
  

}

export default Index;

 

And my server.js file looks like this (please have patience with some of the commented code, everything from thing to find a solution before writing this post)

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:\/\//, ""),
  API_VERSION: ApiVersion.October20,
  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({
      accessMode: "offline",
      async afterAuth(ctx) {
        // Access token and shop available in ctx.state.shopify
        const { shop, accessToken, scope } = ctx.state.shopify;
        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}`);
      },
    })
  );

  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;

    // 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);
    }
  });

  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('/api/products', async (ctx) => {
    try {
      console.log("Inside router executing fetch...");
      const results = await fetch("https://" + "storename.myshopify.com" + "/admin/api/2020-10/" + "orders" + ".json", {
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Access-Token": ctx.state.shopify.accessToken,
        },
      })
      .then(response => response.json())
      .then(json => {
        console.log("JSON object: ", json)
        return json;
      });
      ctx.body = {
        status: 'success',
        data: results
      };
    } catch (err) {
      console.log(err)
    }
  })

  router.get("(/_next/static/.*)", handleRequest); // Static content is clear
  router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
  router.get("(.*)", verifyRequest({accessMode: 'offline'}), handleRequest); // Everything else must have sessions (NEW FOR FORUM)
  //router.get("(.*)", verifyRequest(), handleRequest); // Everything else must have sessions

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

 
And here's the log from the console when pushing the butting in the embedded app:
Inside router executing fetch...
┃ JSON object: {
┃ errors: '[API] Invalid API key or access token (unrecognized login or wrong password)'
┃ }

 

I've been stuck at this stage for days, any help is much appreciated!

Thanks!

Reply 1 (1)

IAmADev
Shopify Partner
20 0 14

Hey !
Did you manage to solve your issue ? I think I'm facing the same problem ://