Introduction:
I am new to using Node. I have followed the Guide: https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react
I used the Shopify CLI to create a Node app
I am having problems calling my own API route from my index.js page. I am making a get request to an URL similar to https://123456789.ngrok.io/api/stores/my-development-shop.myshopify.com
I get the following response: Error: Request failed with status code 400 - Bad Request due to somehow my client iframe page that is rendered by index.js has not authenticated.
How do I authenticate my page so I can call my api?
Note: I am able to retrieve the data by typing in my authenticated browser window:
https://my-develop-store.myshopify.com/admin/apps/APP-ID/api/stores/my-develop-store.myshopify.com
I even tried adding the Cookies from the ctx into the header of my request.
Error:
Error: Request failed with status code 400
...
{
config: {
url: 'https://123456789.ngrok.io/api/stores/my-develop-store.myshopify.com',
method: 'get',
headers: {
Accept: 'application/json',
Cookies: 'shopifyNonce=160078696775500;shopifyNonce.sig=5FoocFlgMKZ2YrgmhBqgSN-o0Eg;shopOrigin=my-develop-store.myshopify.com;shopOrigin.sig=6reeQR8RvnjRchwYFuG-kOl480Q;koa:sess=eyJzaG9wIjoicGF5ZGF5LWRldmVsb3AubXlzaG9waWZ5LmNvbSIsImFjY2Vzc1Rva2VuIjoic2hwYXRfOTI1YjExMjdlNWY1NTU3ODNhNWU5N2M5MDJhYjcwNTAiLCJfZXhwaXJlIjoxNjAwODczMzcwMDY0LCJfbWF4QWdlIjo4NjQwMDAwMH0=;koa:sess.sig=1d2AeB5GMmoEBdwD5bB_0MHU8HM',
'User-Agent': 'axios/0.20.0'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
data: undefined
},
request:
**Files:**
**server/server.js**
```javascript
import "@babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
import graphQLProxy, { ApiVersion } from "@shopify/koa-shopify-graphql-proxy";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
import session from "koa-session";
import * as handlers from "./handlers/index";
import { receiveWebhook } from "@shopify/koa-shopify-webhooks";
import { orderPaid } from "../webhooks/orders/paid";
import { customerDataRequest } from "../webhooks/customers/data-request";
import { customerRedact } from "../webhooks/customers/redact";
import { shopRedact } from "../webhooks/shops/redact";
import { getStoreInfoById } from "../api/stores/store";
import fetch from "isomorphic-fetch";
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();
const { SHOPIFY_API_SECRET, SHOPIFY_API_KEY, SCOPES, HOST } = process.env;
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
const webhook = receiveWebhook({
secret: SHOPIFY_API_SECRET,
});
server.use(
session(
{
sameSite: "none",
secure: true,
},
server
)
);
server.keys = [SHOPIFY_API_SECRET];
server.use(
createShopifyAuth({
apiKey: SHOPIFY_API_KEY,
secret: SHOPIFY_API_SECRET,
scopes: [SCOPES],
async afterAuth(ctx) {
//Auth token and shop available in session
//Redirect to shop upon auth
const { shop, accessToken } = ctx.session;
await handlers.registerWebhooks(
shop,
accessToken,
"ORDERS_PAID",
"/webhooks/orders/paid",
ApiVersion.October19
);
ctx.cookies.set("shopOrigin", shop, {
httpOnly: false,
secure: true,
sameSite: "none",
});
ctx.redirect("/");
},
})
);
server.use(
graphQLProxy({
version: ApiVersion.October19,
})
);
router.get("/", verifyRequest(), async (ctx) => {
if (typeof ctx.query.shop !== "undefined") {
await app.render(ctx.req, ctx.res, "/index", ctx.query);
ctx.respond = true;
ctx.res.statusCode = 200;
}
});
router.get("/api/stores/:store", verifyRequest(), async (ctx) => {
// example: /api/stores/my-develop-store.myshopify.com
// Check if the request is made by the right store
console.log("Route called: /api/stores/:store");
const { shop, accessToken } = ctx.session;
if (ctx.params.store === shop) {
await getStoreInfoById(ctx)
.then((storeInfo) => {
if (typeof storeInfo !== "undefined") {
ctx.body = JSON.parse(JSON.stringify(storeInfo));
ctx.set('Content-Type', 'application/json');
ctx.respond = true;
ctx.res.statusCode = 200;
}
})
.catch((err) => {
ctx.res.statusCode = 500;
console.log(err);
});
} else {
ctx.res.statusCode = 403;
}
});
router.post("/webhooks/orders/paid", webhook, (ctx) => {
orderPaid(ctx.state.webhook);
ctx.res.statusCode = 200;
});
router.post("/webhooks/customers/data_request", webhook, (ctx) => {
customerDataRequest(ctx.state.webhook);
ctx.res.statusCode = 200;
});
router.post("/webhooks/customers/redact", webhook, (ctx) => {
customerRedact(ctx.state.webhook);
ctx.res.statusCode = 200;
});
router.post("/webhooks/shops/redact", webhook, (ctx) => {
shopRedact(ctx.state.webhook);
ctx.res.statusCode = 200;
});
router.get("*", verifyRequest(), async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
});
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
pages/index.js
class HomePage extends React.Component {
// Prepares the props sent to the constructor
// Server sends the ctx.query as the parameter
static async getInitialProps(ctx_query) {
const host = ctx_query.req.headers.host;
const shop = ctx_query.query.shop;
// If executed on Server Side
if (typeof shop === "undefined" || typeof host === "undefined") {
return {};
}
// console.log("host:", host);
// console.log("shop:", shop);
// console.log(ctx_query);
let props = {
shop: shop,
};
// const url_query = ctx_query.asPath.substring(1);
console.log(url_query);
const url = "https://" + host + '/api/stores/' + shop;
console.log("Endpoint URL:", url);
console.log("");
axios.get(url, { headers: { 'Accept': 'application/json' } })
.then((response) => {
console.log("Response Successful")
if (typeof response.data !== "undefined") {
console.log("Data from response:", response.data);
}
})
.catch((error) => {
// handle error
console.log(error); // I always get an 400 Bad Request
})
.finally(() => {
// always executed
});
return props;
}
...
}