ScriptTag 419: unexpected token error

Shopify Partner
5 0 1

Hey Guys, I really need help. I followed this (https://help.shopify.com/en/api/tutorials/build-a-shopify-app-with-node-and-react) Shopify tutorial to create an app to learn some app building skills. Now I'm trying to add script_tag to insert a script file but it's just not working. I'm receiving 419: unexpected token at 'object Object]' error.

 

This is my server.js code:

const Koa = require('koa');
const next = require('next');
const { default: createShopifyAuth } = require('@shopify/koa-shopify-auth');
const dotenv = require('dotenv');
const { verifyRequest } = require('@shopify/koa-shopify-auth');
const session = require('koa-session');
const serve = require('koa-static');
require('isomorphic-fetch');
const Router = require('koa-router');
dotenv.config();
const { default: graphQLProxy } = require('@shopify/koa-shopify-graphql-proxy');
const processPayment = require('./server/router');
const validateWebhook = require('./server/webhooks');

const bodyParser = require('koa-bodyparser');

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


app.prepare().then(() => {
	const server = new Koa();
	const router = new Router();

	server.use(session(server));
	server.keys = [SHOPIFY_API_SECRET_KEY];

	server.use(serve(__dirname + '/public'));

	router.post('/webhooks/products/create', bodyParser(), validateWebhook);

	router.get('/', processPayment);

	server.use(
	  createShopifyAuth({
	    apiKey: SHOPIFY_API_KEY,
	    secret: SHOPIFY_API_SECRET_KEY,
	    scopes: ['read_products', 'write_products', 'write_script_tags'],
	    async afterAuth(ctx) {
	        const { shop, accessToken } = ctx.session;
	        ctx.cookies.set('shopOrigin', shop, { httpOnly: false })

	        const stringifiedBillingParams = JSON.stringify({
	         recurring_application_charge: {
	           name: 'Recurring charge',
	           price: 0.01,
	           return_url: TUNNEL_URL,
	           test: true
	         }
	        });
	        const stringifiedWebhookParams = JSON.stringify({
	          webhook: {
	            topic: 'products/create',
	            address: `${TUNNEL_URL}/webhooks/products/create`,
	            format: 'json',
	          },
	        });
	        const scriptTagBody = {
	            "script_tag": {
	                "event": "onload",
	                "src": `https://${shop}/js/gadjen_app.js`
	            }
	        }
			const scriptTagHeaders = {
				'X-Shopify-Access-Token': accessToken,
				'Content-Type': 'application/json',
				'Accept': '*/*'
			};
	        const options = {
	        	method: 'POST',
	        	body: stringifiedBillingParams,
	        	credentials: 'include',
	        	headers: {
	        	  'X-Shopify-Access-Token': accessToken,
	        	  'Content-Type': 'application/json',
	        	},
	        };
	        const addScriptTagOptions = {
	        	method: 'POST',
	        	credentials: 'include',
	        	body: scriptTagBody,
	        	headers: scriptTagHeaders,
	        	json: true
	        };
	        const webhookOptions = {
	        	method: 'POST',
	        	body: stringifiedWebhookParams,
	        	credentials: 'include',
	        	headers: {
	        	  'X-Shopify-Access-Token': accessToken,
	        	  'Content-Type': 'application/json',
	        	},
	        };

	        fetch(`https://${shop}/admin/webhooks.json`, webhookOptions)
	          .then((response) => response.json())
	          .then((jsonData) =>
	            console.log('webhook response', JSON.stringify(jsonData)),
	          )
	          .catch((error) => console.log('webhook error', error));

	       	const confirmationURL = await fetch(
	         `https://${shop}/admin/recurring_application_charges.json`, options)
	         .then((response) => response.json())
	         .then((jsonData) => jsonData.recurring_application_charge.confirmation_url)
	         .catch((error) => console.log('error', error));
	         ctx.redirect(confirmationURL);

	       	fetch(`https://${shop}/admin/script_tags.json`, addScriptTagOptions)
	         .then((response) => response.json())
	         .then((jsonData) => 
	         	console.log(jsonData.error),
	         );
	    },
	  }),
	);

	server.use(graphQLProxy());
	server.use(router.routes());
	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}`);
	});
});

Please give me some ideas.

0 Likes
Shopify Partner
5 0 1
	        const scriptTagBody = {
	            "script_tag": {
	                "event": "onload",
	                "src": `https://${shop}/js/gadjen_app.js`
	            }
	        }

part of the code, 'src' should be `https://${TUNNEL_URL}/js/gadjen_app.js` which has been updated but still the issue persists :(

0 Likes
Shopify Partner
5 0 1

Alright guys, I don't know why but this solved the issue:

 

	        const scriptTagBody =  JSON.stringify({
	            script_tag: {
	                event: 'onload',
	                src: `${TUNNEL_URL}/js/gadjen_app.js`
	            },
	        });
0 Likes
Shopify Partner
5 0 1

I don't know why but this solved the issue:

 

	        const scriptTagBody =  JSON.stringify({
	            script_tag: {
	                event: 'onload',
	                src: `${TUNNEL_URL}/js/gadjen_app.js`
	            },
	        });

 

0 Likes
Highlighted
Shopify Partner
97 4 32

Just to follow up since this is not very well explained in the tutorial - this is what the problematic fetch in question looks like:

	       	fetch(`https://${shop}/admin/script_tags.json`, addScriptTagOptions)
	         .then((response) => response.json())
	         .then((jsonData) => 
	         	console.log(jsonData.error),
	         );
	    },


And this is what goes to the fetch request as its parameters: 

 

	        const addScriptTagOptions = {
	        	method: 'POST',
	        	credentials: 'include',
	        	body: scriptTagBody,
	        	headers: scriptTagHeaders,
	        	json: true
	        };

 

Note that this is fine - fetch accepts all these parameters via its API and appending them to the request as an object is totally the right way of doing it.

 

The problem lies, as you rightfully mentioned, with the POST's body that is defined here:

	        const scriptTagBody =  JSON.stringify({
	            script_tag: {
	                event: 'onload',
	                src: `${TUNNEL_URL}/js/gadjen_app.js`
	            },
	        });

Before, when you didn't have JSON.stringify() around the script_tag, you were actually sending a JavaScript object as the body. This is something the Shopify API endpoint at 

https://${shop}/admin/script_tags.json

Does not understand. If you look at its documentation here: https://help.shopify.com/en/api/reference/online-store/scripttag#create-2019-10 - it expects a JSON object to be passed along to it, which the JSON.stringify() function takes care of for you.

 

Does that makes sense? If not I can elaborate further. I do agree that the error message is not indicative of the core problem, though, and it would be nice to have another validation on the endpoint's side that notifies you properly about malformed POST requests.

 

Building custom, public-facing Shopify Apps for various niches. Ping me with your app ideas!

My Apps:
* Countries We Ship To Button (Free!) - Show your customers where you ship to with a crisp button + pop-up!


My Tutorials:
* Get Notified When New Comments Are Added To Your Shopify Blog (Free!) - A quick & easy tutorial to set up a free web-service that e-mails you when you get a new comment on your Shopify blog!
0 Likes