How to develop Shopify App with Node.js
Top

Shopify App tutorial for Nodejs

To start developing you should register as a partner to have access to Partner Dashboard. There you can create a developer’s store and use it to develop and test applications.

More detailed information here.

OAuth 2.0 authorization mechanism is required. Authorization allows the installation of applications to your shop and making API requests.

There are several libraries to ease OAuth authorization implementation.

Private Authorization is used for interactions between private applications and a store.

More detailed information about authorization mechanisms can be found here.

Creating an application.

In this example we will create an application that will make changes to our store’s theme. It will create a new jc-map page (generating a script and template) with Google Maps and the store’s pin location (data will be retrieved from Location).

Requirements:

Application code can be found here

Step 1: Making the application accessible via Internet.

  1. Download ngrok
  2. Go to ngrok directory
  3. Execute command:
    ./ngrok http 3000 (MacOs/Linux) or ngrok http 3000 (Windows)

Express uses port 3000 by default.

Every time the ngrok is executed, a new subdomain is created (e.g. APP_URL=59998a8.ngrok.io)

Step 2: Making the application scaffold.

  1. Execute commands in the application directory for the generation of scaffolding:
    $ npm install express-generator -g
    $ express--view=pug
    
    
  2. Delete unnecessary files
    ./routes/users.js
  3. Update file ./app.js and erase unused code (highlighted with darker background).
    Shopify
  4. Create files:
    ./.env – configs.
    ./routes/jctest.js – routes.
    ./services/shopify-stores.js – routes.
  5. Update ./app.js with highlighted code:
    Shopify
  6. Install dotenv module to work with .env file:
    npm install dotenv –save
  7. Update ./app.js
    Shopify
  8. Install shopify-node-api module to ease interactions in Shopify Admin API by typing command
    npm install shopify-node-api –save

Step 3: Application settings.

  1. Authorize in Shopify Partner Account.
  2. Click Apps then choose your application from the applications list.
  3. Click Get API credentials.Shopify
  4. Copy the API Key and API Secret, to .env file as follows:
    			API_KEY=YOUR_API_KEY
    API_SECRET=YOUR_SECRET_KEY
    APP_URL=YOUR_APP_URL
    
    

    Get YOUR_APP_URL from Step 1

Setting app URLs

In your Partner dashboard:

  1. Click Apps
  2. Choose your application from the applications list.
  3. Click App info.
  4. In App URL field, type https://#{app_url}>/jctest/install
  5. In Whitelisted redirection URL, type https://#{app_url}/jctest/auth Shopify

Step 4: Implementation of the main application logic

Now as all preparations are complete you can proceed to create your application for Shopify.

This module emulates saving authorization data and storing map configuration for the specified store. The module is simplified as possible.

shopify-stores.js

	const ShopifyAPI = require('shopify-node-api');
const mapStyles = require('./map-styles');


module.exports = {
   stores: {},
   mapConfiguration: {},
   install (storeOptions) {
       let store = this.stores[storeOptions.shop];

       if (!store) {
           store = new ShopifyAPI(storeOptions);
           this.stores[storeOptions.shop] = store;
       }

       return store;
   },
   shopNameFormater (shop){
       return shop.replace(/https?:\/\//, '');
   },
   getStore (shop) {
       shop = this.shopNameFormater(shop);
       return this.stores[shop];
   },
   saveMapConfiguration (shop, configurations) {
       shop = this.shopNameFormater(shop);
       let mapConfigurations = this.mapConfiguration[shop];
       if (!mapConfigurations) {
           mapConfigurations = {};
           this.mapConfiguration[shop] = mapConfigurations
       }
       mapConfigurations.mapApi = configurations.mapApi;
       mapConfigurations.location = configurations.location;
       let mapStyle;
       if(configurations.mapStyle){
           mapStyles[configurations.mapStyle]
       }else if(configurations.mapStyleCustom){
           mapStyle = JSON.parse(configurations.mapStyleCustom);
       }else{
           mapStyle =mapStyles[Object.keys(mapStyles)[0]];
       }
       mapConfigurations.styles = mapStyle;
       return mapConfigurations;
   }
}

install – method that saves a new store connection object or returns an existing one.
storeOptions – configuration object to create ShopifyAPI instance.
getStore – returns saved ShopifyAPI instance for authorized store.
shop – store name.
saveMapConfiguration – saves map settings for specified store.
shop – store name.
configurations – map settings object (google API key, map styles)

Module that generates a jc-map.js file for map initialization and settings, page.jc-map.liquid template for the page containing map.

map-theme-construct-helper.js

 	module.exports = {
   generateMspScript (location, mapStyle) {
       return `
$(function () {
   var city = '${location[0].city}';
   var geocoder = new google.maps.Geocoder();
   geocoder.geocode({ 'address': city}, function(results, status) {
       console.log(results)
       var location = results[0].geometry.location;
       var map = new google.maps.Map(document.getElementById('map'), {
           zoom: 12,
           center: location,
           styles: ${JSON.stringify(mapStyle)}
       });
       var marker = new google.maps.Marker({
           position: location,
           map: map
       });
   });
});
`;
   },
   generateMspHtml (googleApiKey) {
       return `
<div id="map-container">

&nbsp;
<div>{{ page.content }}</div>
&nbsp;

<style>
       #map {<br />           height: 400px;<br />           width: 100%;<br />       }<br />   </style>&nbsp;
<div id="map"></div>
&nbsp;

</div>
<script src="https://maps.googleapis.com/maps/api/js?key=${googleApiKey}"></script> <script src={{ 'jc-map.js' | asset_url }} defer="defer"></script> `; } } 

generateMspScript – method that generates the text of the added script to initialize and configure the google map.
location– location taken from settings of the store.
mapStyle – style settings for Google Maps.
generateMspHtml – method that generates HTML for the page template.
googleApiKey – key to access the Google Maps API.

Auxiliary module with ready styles for maps that we can borrow from snazzymaps

map-styles.js

Setting routes.

jctest.js

	const express = require('express');
const router = express.Router();

const shopifyStoresService = require('../services/shopify-stores');
const constructHelper = require('../services/map-theme-construct-helper');
const mapStyles = require('../services/map-styles');



router.get('/install', function (req, res) {
   var shop = req.query['shop'];
   var scopes = 'read_orders,read_products,read_themes,write_themes,write_content';
   var shopify = shopifyStoresService.install({
       shop: shop, // MYSHOP.myshopify.com
       shopify_api_key: process.env.API_KEY, // Your API key
       shopify_shared_secret: process.env.API_SECRET, // Your Shared Secret
       shopify_scope: scopes,
       redirect_uri: `https://${process.env.APP_URL}/jctest/auth`,
       nonce: '' // you must provide a randomly selected value unique for each authorization request
   });
   //Generate url for the application installation screen
   var installUrl = shopify.buildAuthURL();
   res.redirect(installUrl);
})

The first route defined in the application is used to redirect the merchant when they click Get in the Shopify App Store. This address you defined earlier in your Partner Dashboard as the Application URL.

The merchant gets redirected to the application installation screen when navigating to this URL. Something like this can be seen:
Shopify

We form installUrl using shopify-node-api

	http://${shop}/admin/oauth/authorize?client_id=${API_KEY}&amp;scope=${scopes}&amp;redirect_uri=https://${API_URL}/jctest/auth
  • client_id – API_KEY of our application
  • shop – merchant’s store URL
  • scopes Permission scopes required for the application (in this case, the application needs permission to read orders, read products, and write products).
  • redirect_uri The redirect_uri parameter, which is where the merchant will be redirected after they authorize the installation. This should match the URL defined in the partner dashboard as the Redirection URL. In this case, the merchant is redirected to /jctest/auth.

After /jstest/install is done – we need to authorize and get the Auth token.

	router.get('/auth', function (req, res) {
   let shop = req.query['shop'];
   let shopify = shopifyStoresService.getStore(shop);
   shopify.exchange_temporary_token(req.query, function (err, data) {
       shopify.get('/admin/themes.json', function (err, data, headers) {
           res.render('index', {
               title: 'jc map app',
               appKey: process.env.API_KEY,
               shop: shop,
               themes: data.themes,
               mapStyles: Object.keys(mapStyles)
           });
       });
   });
});

The last route we’re concerned about is /jstest/create-map which manages the addition of pages and necessary scripts to the template.

	router.post('/create-map', function (req, res) {
   const shop = req.query['shop'];
   const shopify = shopifyStoresService.getStore(shop);
   if (!shopify) {
       return;
   }

   let mapConfigurations = shopifyStoresService.saveMapConfiguration(shop, req.body);
   //Getting info about themes in store
   shopify.get('/admin/themes.json', { role: "main" }, function (err, data, headers) {
       if (err || !data.themes.length) {
           return;
       }
       let id = data.themes[0].id;// Main theme id
       //  Create map page template
       shopify.put(`/admin/themes/${id}/assets.json`, {
           asset: {
               key: 'templates/page.jc-map.liquid',
               value: constructHelper.generateMspHtml()
           }
       }, function (err, data, headers) {
           // Get store locations info
           shopify.get('/admin/locations.json', function (err, data, headers) {
               if (err) {
                   res.status(500).end(err);
                   return;
               }
               const location = data.locations;
               // Add script asset for google map initialization
               shopify.put(`/admin/themes/${id}/assets.json`, {
                   asset: {
                       key: 'assets/jc-map.js',
                       value: constructHelper.generateMspScript(location, mapConfigurations.styles)
                   }
               }, function (err, data, headers) {
                   if(err){
                       res.status(500).end(err);
                       return;
                   }
                   // Creating map page in store
                   shopify.post('/admin/pages.json', {
                       "page": {
                           "title": "Map",
                           "body_html": "Map page",
                           "published": true,
                           "template_suffix": 'jc-map' 
                             } },
                  function (err, data, headers) { 
                     if(err){ res.status(500).end(err);
                       return;
                     } res.status(200).end();
                  }); 
                }); 
              }); 
            }); 
          }); 
  });

Step 5: Creating application interface.

We will use Embedded App SDK for that.

Embedded App SDK has a secure API window.postMessage, that gives us access to the admin panel:

  • Modal windows.
  • Dialog boxes for warnings, confirmations and input.
  • Header panel with logo, breadcrumbs, buttons, pagination and drop-down menu.
  • Pop-ups.

Firstly, we need to make Embedded App SDK accessible for our application:

  1. From Partner dashboard, click Apps.
  2. Choose your application from the list.
  3. Click Extensions tab.
  4. In Embed in the Shopify Admin section click Enable.
  5. Click Save at the top to apply changes to your application.

Shopify

Initialization

In our application we use the pug template engine

To access the JavaScript API we need to plug-in app.js (./public/javascripts/). SDK should be initialised right after we use app.js.

We need to add the following code in layout.pug file to connect to Embedded App SDK:
layout.pug

	html
 head
   title= title
   link(rel='stylesheet', href='/stylesheets/style.css')
   script(src='https://cdn.shopify.com/s/assets/external/app.js')
   script.
     ShopifyConfig = {
       apiKey: '#{appKey}',
       shopOrigin: 'https://#{shop}'
     };
     ShopifyApp.init(ShopifyConfig);
   script(src='https://code.jquery.com/jquery-3.2.1.min.js' integrity='sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=' crossorigin='anonymous')
   script(src='/javascripts/app.js')
 body
   block content

Next implement the form that adds a page with Google Maps to our Online Store and refresh the inpex.pug template content

inpex.pug

	extends layout

block content
 h1= title 
 div
   div
     span Map will be added to
     span.theme-title= themes[0].name
     span theme
   .validation-notification
   .map-page-configuration
     form#map-config
       div
         label(for="apiKey") Google Map api key
         input(type="text" name="apiKey")
       div
         label(for="mapStyle") Input your map custom styles
         textarea#map-style(name="mapStyleCustom", cols="30", rows="10")
       div
         p Or choose from list
         ul.msp-styles-list
           each mapStyle in mapStyles
             li
               label(for=mapStyle)
                 input(type="radio", name="mapStyle" class="map-style-radio" id=mapStyle value= mapStyle)
                 figure.figure.style-example
                   img.figure-img.img-fluid.rounded(src="/images/" + mapStyle + ".png", alt= mapStyle)

At the end we should get a humble interface

Shopify

Images for proposed map stylizations ./public/images .

bentley.png

Shopify

clean-cut.png

Shopify

midnight-commander.png

Shopify

subtle-grayscale.png

Shopify

ultra-light-with-labels.png

Shopify

Step 6: Start application.

After all settings are done we can run our application.

  1. Open terminal window.
  2. Go to application directory.
  3. Run npm start.

Step 7: Install.

Install your app on a test store.

You can also install your app on a test store to test its behavior in the Shopify admin.

Log into your Partner dashboard.

Click Apps.

Click the name of the new app you’ve created.

Click Create App Store listing.

Click Save.

Click View app listing. The listing page for your app loads in the Shopify App Store.

Click Get to install the app to your test store.

Summary

This was simple example of shopify embedded app on node.js introduced you with creating applications for shopify platform.
Good luck guys!

  • Zeeshan Ahmed

    Hey i have cloned your project, created my own app as well updated everything accordingly
    but on app installation am redirecting to admin/apps

    • Des Deso

      Hi Ahmed

      Do not know is it still working. 2 years passed. Need, time to check

    • Zeeshan Ahmed

      Thanks buddy I have resolved the
      issue but now I need to know some thing else can you please help?

    • Des Deso

      Sure. What is it?

    • Zeeshan Ahmed

      Actually I need to expose button on each product page using this app, through which I need to make an external api call.
      Can you please guide me with the steps so this will save my alot of time.

    • Des Deso

      I will take a look on Monday with a guy who did that cause I am more frontend guy 🙂

    • Zeeshan Ahmed

      Actually I need to expose button on each product page using this app, through which I need to make an external api call.
      Can you please guide me with the steps so this will save my alot of time.

up

Please turn your device