How to render HTML in my theme via app created with React

Highlighted
Tourist
11 1 13

Hello, I recently completed this tutorial (https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react) and then used the skills gained there to create the basic admin interface for my app. Now that I am finished with that, I would like to move to the front end development portion of the project, but I am having trouble finding a tutorial or guide that will help me access my app's data from the front end and output a list of products based on stored IDs. The output part should be easy (I have fairly extensive experience with Shopify theme development and modification), but accessing my app from the front end is a mystery to me.

I am seeing posts that mention ScriptTags...but the documentation on that is a 404 (https://shopify.dev/docs/admin-api/rest/reference/online_store/scripttag). 

 

I am assuming that the basic configuration will be:

1. Add a script tag to the header or footer of my theme
2. Add an HTML element that I can select for code injection
3. Give my script the ability to request data stored by my app
4. Give my script the ability to add HTML to the element from #2 on initialization

Where I am stuck is, how do I make a script file in my app accessible from the storefront? I am currently running a tunnel via ngrok to my local machine, but I can't just call up https://mysubdomain.ngrok.io/myscript.js. If I do, it redirects to the Shopify admin area with a 404. I imagine that this has to do with the proper means of configuring the routing in my app to allow direct access to certain public files, like myscript.js.

Once I make the script file accessible, how can I safely query data from my app or store? I realize that there are some security issues here so any direction on how to do this properly would be greatly appreciated.

I am using React and I am a PHP developer who is coming over to React for the first time outside of a course that I took which really only covered making basic UI components, so there is likely stuff regarding the routing or general server configuration that I just don't understand. Thanks in advance for your help!

2 Likes
Highlighted
Shopify Partner
347 25 51

Hi @mert86 Here's a working link for ScriptTags:

 

https://shopify.dev/docs/admin-api/rest/reference/online-store/scripttag

 

Accessing your apps data from the storefront you have to use an app proxy. So there's no CORS issues. You can then use the script tag to make a call to your own API requesting that app data. 

 

All the best,

 

Sam 

Custom Apps and Themes Support | Email us: hello@achieveapplabs.com | Follow us on Twitter @achieveapplabs | New Monthly Support by Achieve Applabs www.achieveapplabs.com
1 Like
Highlighted
Tourist
11 1 13

Thanks @achieveapplabs! Headed down that rabbit hole now and will post a response soon.

0 Likes
Highlighted
Tourist
11 1 13

I solved the majority of this challenge, and am so excited that I might just crack open an afternoon beer! Big thanks to @achieveapplabs for taking time to point me in the right direction. 

I wanted to post my solution below so that anyone who happens upon this thread might be able to use my process as part of their solution. Here we go...

Where I am stuck is, how do I make a script file in my app accessible from the storefront? 
I am going to use the tutorial located at https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react as a starting point for this, as many of the people who complete that tutorial are going to be left with a React app that they might not understand entirely, and that app really only is available via the admin interface. To make a script in your app available online (and thus accessible from the storefront), you need to complete the following tasks:

1. Set up an application proxy app extension
This tutorial (https://shopify.dev/tutorials/display-data-on-an-online-store-with-an-application-proxy-app-extensio...) does a pretty good job of explaining how to accomplish this, but it had a few sticking points that I think need to be clarified. Follow their instructions from steps 1-5 and then return here to discuss the proxy settings. Let's take a look at the (placeholder) settings that I used:


Annotation 2020-02-27 134322.jpg

So what this does, is it takes traffic from https://my-test-store.myshopify.com/apps/my-subpath and forwards it to https://reserved-subdomain.ngrok.io/test (it is a bit more complicated than that but for now that's all that matters). In the Shopify tutorial, they mention an app_path being part of the URL, which wasn't something that I needed in this case. I imagine that if I have multiple apps on a server then it comes into play, but if you are just adding to the React App tutorial, this will work.

2. I went ahead and paid for the ability to have reserved subdomains with ngrok
That way, I don't have to update the URLs in my app settings or in the proxy each time I open a new tunnel. Not a critical part of this solution, but for like 8 bucks a month...that's an easy buy.

3. Configure routing in your app so that requests for /test are handled properly
So far we have configured our proxy so that https://my-test-store.myshopify.com/apps/my-subpath is forwarded to https://reserved-subdomain.ngrok.io/test, but the app created in the tutorial is not configured to handle a request for /test. If you followed the React tutorial to the end, then you will have koa-router installed, giving you everything you need to forward /test to a script of your choice. For this example, create a directory in the root of your app named "scripts," and in that directory, create a file named testscript.js and add the following code to that file:

async function runTestRoute (ctx, next) {
	ctx.body = 'Greetings, I am test route'
};
module.exports = runTestRoute;

Then, open your server.js file in the root of your app, and add the following line* (line 15 in my app) 

  const Router = require('koa-router');
  const processPayment = require('./server/router');
+ const runTestRoute = require('./scripts/testscript');

*The first two lines are already there, but were just included to give you a frame of reference for where you can add the lines, and the + is just there to denote a new line, don't actually include the + in your code, just add const runTestRoute = require('./scripts/testscript')

Then update the same server.js file with the second line here (line 31 in my app)

  router.get('/', processPayment);
+ router.get('/test', runTestRoute);


If your app is already running, hit CTRL+C (or whatever the mac equivalent is if that's your bag) in your cmd/terminal window to shut down the app (assuming this is a development server and nobody is actually using it). Then enter npm run dev to fire it back up, and then run the following tests once it says it is ready on 3000:

Visit your ngrok URL. In this example it is https://reserved-subdomain.ngrok.io/test
You should see "Greetings, I am test route" displayed on your screen. If not, then you might have made a mistake in part 3 of this answer.

If the previous test works, visit your proxy URL. In this example that is https://my-test-store.myshopify.com/apps/my-subpath
You should see "Greetings, I am test route" displayed on your screen. If not, then you might have made a mistake in part 1 of this answer.

Note that anytime you make changes to your server.js file, you have to restart the server to view the updated app in a browser. There is supposedly a way around that via nodemon but I don't have any experience with that.

Hopefully this helps to bail someone out who gets stuck in the same spot I did. If you have any feedback or would like to suggest an edit to this answer please feel free to comment below. I am not yet an expert in Node development or Shopify development, so please feel free to make suggestions or recommendations!

Cheers

4 Likes
Highlighted
Shopify Partner
347 25 51

Hi @mert86,

 

I read your post and it's very detailed and will definitely help others! Crack that beer open because I know the feeling. One thing to point out is you will want to verify the proxy request as well. Here is a working example we use:

 

require('isomorphic-fetch');
require('dotenv').config();

var querystring = require('querystring');

const path = require('path');


const AchieveDB = require('../db');

const crypto = require('crypto');


module.exports = (function(){

	return function(request, response, next){

		const {SHOPIFY_APP_SECRET} = process.env;

		var hash, input, query, query_string, ref, ref1, ref2, signature;
		query_string = (ref = (ref1 = request.url.match(/\?(.*)/)) != null ? ref1[1] : void 0) != null ? ref : '';
		query = querystring.parse(query_string);
		signature = (ref2 = query.signature) != null ? ref2 : '';
		delete query.signature;
		input = Object.keys(query).sort().map(function(key) {
		var value;
		value = query[key];
		if (!Array.isArray(value)) {
		  value = [value];
		}
		return key + "=" + (value.join(','));
		}).join('');
		hash = crypto.createHmac('sha256', SHOPIFY_APP_SECRET).update(input).digest('hex');
		if (signature !== hash) {
		response.status(403).send("Signature verification for shopify proxy request failed");
		} else {
		next();
		}
		return null;

	}


})();

Also kudos for using Koa, it takes time to migrate and learn so you're steps ahead with Koa. 

 

All the best,

 

Sam

Custom Apps and Themes Support | Email us: hello@achieveapplabs.com | Follow us on Twitter @achieveapplabs | New Monthly Support by Achieve Applabs www.achieveapplabs.com
2 Likes
Highlighted
Tourist
11 1 13

Thanks Sam! I don't know that any developer would survive without the wealth of knowledge that is shared in message boards across the web. Lord knows Stack Exchange has both saved my bacon a many times over the years. It also gives us the opportunity to see others provide feedback/criticisms of accepted answers, so you can go from a decent answer, to an efficient answer, to a secure and efficient answer all in one thread.

One of the worst things is when you find a post where an author asks a valid question, then replies with "Great I figured it out!" and doesn't provide a detailed response as to why it worked and how they implemented it. So I am going to try to document as many of these questions as I run into (as time allows), hopefully it helps a few people out of the weeds.

Also, this script is EXACTLY what I was just about to dive into. I will plug it into the app and let you know if I run into any issues :)

Cheers,
Merritt

2 Likes
Highlighted
New Member
2 0 0

Hi @mert86, how did it go, did you manage to get the html rendered on the front end, I am stuck at the same stage here. 

0 Likes
Highlighted
New Member
1 0 1

You literally just saved my life, you have no clue how much time I sunk into trying to figure this out! BLESS YOU!!!!!!!!

1 Like
Highlighted
New Member
2 0 0

Hi @meganspauldingc 

Can you please share your code here, what issues you were facing and how did you manage it?

0 Likes
Highlighted
New Member
2 0 0

Hi @mert86, thanks for the helpful documentation.

Was wondering how you were able to render liquid (for example a collection list of products) that renders on the storefront, matching the theme of the customer?

0 Likes