App reviews, troubleshooting, and recommendations
I'm trying to load a custom JS in the store front, for customers to see, depending on which collections would be selected from the app settings. The issue is that when i try to use ScriptTag, whether for POST, to create a ScriptTag, or GET to read them, i am getting a 403 response.
[Symbol(Response internals)]: {
url: 'https://agecontrol.myshopify.com/admin/api/2021-07/script_tags.json',
status: 403,
statusText: 'Forbidden',
headers: Headers { [Symbol(map)]: [Object: null prototype] },
counter: 0
}
I am attempting this from the server.js, you can see my code here:
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({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
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],
});
fetch(`https://${shop}/admin/api/2021-07/script_tags.json`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken,
}
}).then(async response => {
const data = await response.json();
// check for error response
console.log(response);
try {
if (response.ok) {
// get error message from body or default to response status
// const error = (data && data.message) || response.status;
// return Promise.reject(error);
console.log(`no errorr!!!`);
}
} catch (error) {
this.setState({errorMessage: error.toString()}).bind(this);
console.error('There was an error!', error);
}
}).catch(response => {
console.log(response);
});
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}&host=${host}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
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("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
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);
}
});
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
I'm attempting this method, following an older ticket:
https://community.shopify.com/c/Shopify-APIs-SDKs/Shopify-App-React-Node-Script-Tag-The-script-tag-i...
For future reference, the 403 is caused by the lack of scopes for the app. Now, since others might encounter this issue as well, the scopes are found in the .env file of your app. It normally has
SCOPES=write_products,write_customers,write_draft_orders
I added these as well
SCOPES=write_products,write_customers,write_draft_orders,read_script_tags,write_script_tags
The issue now is that i'm getting a 404 on the ScriptTag loaded. I may not have written the src well
fetch(`https://${shop}/admin/api/2021-07/script_tags.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Shopify-Access-Token': accessToken,
},
body: JSON.stringify({
"script_tag": {
"event": "onload", "src": "https://************.ngrok.io/public/scripttag.js", "display_scope": "all", 'cache': false
}
})
})
Thanks for the explanation. Did you figure out what's up with the 404 error?
In Canada, payment processors, like those that provide payment processing services t...
By Jacqui Mar 14, 2025Unlock the potential of marketing on your business growth with Shopify Academy's late...
By Shopify Mar 12, 2025Learn how to increase conversion rates in every stage of the customer journey by enroll...
By Shopify Mar 5, 2025