How to Create an Embedded Shopify App with Express
There aren’t many tutorials on how to create a Shopify app without using React, specially if you want your app to be a Shopify embedded app and work within Shopify’s admin.
Here’s a simple way to have your app working with express js and html, or your preferred view engine (in my case Handlebars).
Register your app in Shopify’s partner dashboard and install ngrok
Go to partners.shopify.com. Create an account, register an app name, and create an automatic demo development store.
Once you created your Shopify partner account:
-
Intall ngrok and open it on the same port where your Shopify app is running, example
./ngrok http 3000
. You’ll get a temporary url that will work as a proxy between your local 3000 port and the internet. -
Go to “App setup” within your Shopify Partners dashboard and where it says ‘App URL’ paste your ngrok url together with the path we’ll use to authenticate and prompt users to install our app, in my case it was:
https://420bddf05fae.ngrok.io/auth/shopify/
-
In the ‘Allowed redirection URLs’ paste the url where users will be redirected after being authenticated eg.
https://420bddf05fae.ngrok.io/auth/shopify/callback
Install shopify-express-oauth-redirect
npm install shopify-express-oauth-redirect
Follow the readme to create the /auth/shopify/
route.
Now if you go to yourngrokurl.io/auth/shopify?shop=yourdemoshopname.myshopify.com
it should redirect you to a Shopify screen to authorize and install your app in the demo store you created in step 1.
Create the callback route
Now we need to create the callback route, this is where Shopify will redirect users when they install the app successfully, it will also serve as the “home page” of our app.
The callback route should be the same route as the callbackUrl
you defined when setting up the shopify-express-oauth-redirect package, In our case it is auth/shopify/callback
.
For now let’s just create a simple route that will send a message when reached.
router.get("/auth/shopify/callback", async (req, res) => {
res.status(200).send("Success!")
})
To test it, go to yourngrokurl.io/auth/shopify?shop=yourdemoshopname.myshopify.com
approve to install your app and see if you get redirected correctly, you should now see the Success!
message.
Add security to our callback route and get the installed store info.
Let’s improve the callback route a bit. We’ll make sure that the request is actually coming from shopify and we’ll also get access to the user and shop information to save in our database, but first we have to install a few packages:
npm install crypto cookie nonce querystring equest-promise
Now let’s update our /auth/shopify/callback/
route to this:
router.get("/auth/shopify/callback", async (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
// security to make sure the request is coming from shopify
if (state !== stateCookie) {
return res.status(403).send("Request origin cannot be verified");
}
if (shop && hmac && code) {
// DONE: Validate request is from Shopify
const map = Object.assign({}, req.query);
delete map["signature"];
delete map["hmac"];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, "utf-8");
const generatedHash = Buffer.from(
crypto
.createHmac("sha256", process.env.SHOPIFY_API_SECRET_KEY)
.update(message)
.digest("hex"),
"utf-8"
);
let hashEquals = false;
try {
hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac);
} catch (e) {
hashEquals = false;
}
if (!hashEquals) {
return res.status(400).send("HMAC validation failed");
}
//Exchange temporary code for a permanent access token so we can later make api calls to the user shop
const accessTokenRequestUrl =
"https://" + shop + "/admin/oauth/access_token";
const accessTokenPayload = {
client_id: process.env.SHOPIFY_API_KEY,
client_secret: process.env.SHOPIFY_API_SECRET_KEY,
code,
};
const { access_token } = await request.post(accessTokenRequestUrl, {
json: accessTokenPayload,
});
// as an example let's use access token to make API call to 'shop' endpoint and get info about the shop that is trying to install our app
const shopRequestUrl = "https://" + shop + "/admin/api/2020-07/shop.json";
const shopRequestHeaders = {
"X-Shopify-Access-Token": access_token,
};
const shopResponse = await request.get(shopRequestUrl, {
headers: shopRequestHeaders,
});
// finally, let's redirect the user to the index.html where the app's homepage is (in my case I am using handlebars)
res.render('shoify/index');
} else {
res.status(400).send("Required parameters missing");
}
});
now try again intalling your app yourngrokurl.io/auth/shopify?shop=yourdemoshopname.myshopify.com
and you should get redirected to your app’s homepage!
Setup Shopify App Bridge
By this point your app should be working, however when our users are redirected to the callback url it will take them out of shopify to a page on our site.
What we want instead is to show our app embdedded within the Shopify UI, for that we need Shopify’s App bridge.
All we have to do is add a few <script>
tags in our app’s homepage, here is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My shopify APP</title>
<script src="https://unpkg.com/@shopify/app-bridge@^1"></script>
</head>
<body>
<script type="text/javascript">
var AppBridge = window["app-bridge"];
var createApp = AppBridge.default;
var app = createApp({
apiKey: "your apiKey",
shopOrigin: "the shopOrigin",
});
</script>
</body>
</html>
Here we just imported Shopify’s app bridge from the unpkg cdn and initialized it with our apiKey and the shopOrigin.
That’s it! now when you install your app you’ll see your app running within the Shopify UI!
To make your design look native to shopify, I recommend you to check out Polaris which are Shopify’s UI components