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

shopquist
New Member
1 0 0

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!

0 Likes