HLQ Software Logo
Ruby on Rails

Create a custom Shopify metafield app with Ruby on Rails

Quyet Dao
#ruby on rails#Shopify

Why Shopify and Shopify Apps

If you’re excited about Ruby on Rails and the e-commerce market like me, you might know about Shopify and Spree. However, you might have noticed that compared to Spree, Shopify has been growing very fast recently, and there’s always a big demand in this market.

Currently, I am working as a Shopify developer for a Singaporean startup in the coffee industry. I realize that many startup companies use Shopify as their platform to build online stores, at least in this industry. For almost a year, we have worked together to build an impressive Shopify marketplace, continuously adding new features to the web app and upgrading the website’s UI & UX. In most cases, we found a suitable Shopify App from the Shopify App Store.

Later, after launching the project, we started thinking about creating our own Shopify application that would work best for our store and provide us with the freedom to customize. We decided to go for it. In this article, I will share some of my experiences in creating a Shopify custom app.

Types of Applications in Shopify

There are two types of applications that Shopify provides:

In this post, I will talk about public Shopify applications.

Register as Shopify Partner

In order to create a custom Shopify public application, you must register as Shopify partner

First, go to Shopify partner page to register yourself. After registering, create your own Shopify public app

Shopify Partner Page

Then, go to your application => click on App setup and scroll down you will see there’s API Key and API Secret Key Keep in mind we have those keys, and we will use later.

Shopify App Keys

Also, later you need to comeback to this screen to fill in our server where Shopify can access & load the content to embed in it

Shopify App Keys

Create Ruby on Rails Application

As usual, what you need is to initialize the Rails app by command

rails new master-metafields

To make it available for remote access from Shopify, you need to expose your local host to a public online URL. In this post, I introduce you to ngrok.

Setup ngrok

ngrok is a powerful tool for publishing your local application to the Internet. It’s easy to use. You need to to run it on your local. After downloading and setting up ngrok, connect it to port 3000 if our local rails server is running on this port.

ngrok is a powerful tool for publishing your local application to the Internet. It’s easy to use. You need to download it and follow the instruction to run it on your local machine. After downloading and setting up ngrok, connect it to port 3000 if your local Rails server is running on this port.

./ngrok http 3000

Also, please make sure you are running your Rails app with the default command and port: rails s

You will receive a random URL for your local server from ngrok. Copy this URL and use it for your Shopify custom app as shown in the above image.

Please note that if you stop ngrok, your Shopify app will not be able to run. When you start ngrok again, you will receive a new URL and you will need to update it in your Shopify app setup.

Setup shopify_app

Now, the first thing you need to do with your master-metafields project is to add gem shopify_app to your Gemfile and bundle install the new gem

After finish above steps, Use above shopify app api_key and secret to install shopify app

$ rails generate shopify_app --api_key <your_api_key> --secret <your_app_secret>

Above command will install the gem, generate Shop Model and Home Controller and create codes that authenticates your application to Shopify stores.

Setup Env Variables

Edit file config/initializers/shopify_app.rb

ShopifyApp.configure do |config|
  config.application_name = "Your Shopify App Name"
  config.api_key = Rails.application.credentials.shopify_api_key
  config.secret = Rails.application.credentials.shopify_api_secret

  config.scope = "read_orders, write_products"
  # Consult this page for more scope options:
  # https://help.shopify.com/en/api/getting-started/authentication/oauth/scopes

  config.embedded_app = true
  config.after_authenticate_job = false
  config.api_version = "2019-04"
  config.session_repository = Shop
end

Install your Shopify App to your Shopify Store

From your Shopify Partner account, you will see an option to create a store. From there, create a development store for testing purposes.

Shopify Partner Store

After this step, go to the Apps option in the left panel, choose the app we’ve just created, and install it in the development store you’ve just created.

Install development app

Fetch Product from Shopify

It’s time to work with the Shopify API. The first task is to fetch products from our Shopify store. Let’s create products_controller.rb and set the root to products#index.


# routes.rb

Rails.application.routes.draw do
  root :to => 'products#index'
  resources :products
end

# app/controllers/products_controller.rb

class ProductsController < AuthenticatedController
  def index
    @products = ShopifyAPI::Product.find(:all, params: { limit: 10 })
  end
end

If you want to use Bootstrap to style your app like mine, then go ahead, add bootstrap gem into Gemfile, and run bundle install

# Gemfile

gem 'bootstrap'
gem 'jquery-rails'
gem "bootstrap_form"

Import Bootstrap into application.css

/* app/assets/stylesheets/application.scss */

@import "bootstrap";
@import "rails_bootstrap_forms";

Now, continue with your index view app/views/products/index.html.erb

<% content_for :javascript do %>
  <script type="text/javascript">
    ShopifyApp.ready(function(){
      ShopifyApp.Bar.initialize({ title: "Home" });
    });
  </script>
<% end %>

<div class="row" id="products-list">
  <div class="col-md-12">
    <table class="table">
      <thead class="thead-light">
      <tr>
        <th scope="col">#</th>
        <th scope="col">Title</th>
        <th scope="col">Image</th>
        <th scope="col">Type</th>
        <th scope="col">Vendor</th>
        <th scope="col">Actions</th>
      </tr>
      </thead>
      <tbody>
      <% @products.each_with_index do |product, index| %>
        <tr>
          <th scope="row"><%= index + 1 %></th>
          <td><%= product.title %></td>
          <td><%= image_tag(product.image.src, class: 'img-thumbnail small-img-thumbnail', alt: 'Product Image') %></td>
          <td><%= product.product_type %></td>
          <td><%= product.vendor %></td>
          <td>
            <%= link_to "Metafields", product_path(product), class: "btn btn-link" %>

            <%= link_to "Admin", "https://#{@shop_session.domain}/admin/products/#{product.id}", target: "_top", class: "btn btn-link" %>
          </td>
        </tr>
      <% end %>
      </tbody>
    </table>
  </div>
</div>

Create header for your app

<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="navbarNav">
    <div class="navbar-nav">
      <%= link_to "Products", products_path, class: "nav-item nav-link active" %>
    </div>
  </div>
</nav>

Style your app

//HEADER
.navbar .nav-item {
  margin-right: 12px;
  transition: all 0.2s;
  border: 1px solid #CCCCCC;
}

.navbar .nav-link:hover, .navbar .nav-link:focus, .navbar .nav-link.active {
  border-color: #30A54A;
  color: #30A54A;
  background-color: transparent;
  box-shadow: none;
  border-radius: 6px;
}

.navbar {
  border-bottom: 1px solid #DADADA;
}

#page-wrapper {
  padding-top: 80px;
}

body {
  padding: 0px 15px;
  min-width: 800px;
  margin: 0px;
}

// PRODUCT
.small-img-thumbnail {
  max-width: 60px;
}

#product-title {
  margin-left: 2px;
  margin-top: 0px;
  margin-bottom: 20px;
  display: block;
  width: 100%;
  float: left;
}

Hura, now open your Shopify store and look at what we’ve just achieved

shopify embedded store

Fetch Product Metafields

First, install HTTParty gem, and bundle install as normal

# Gemfile
gem 'httparty'

After installing, open your products_controller and create show action for product page

def show
  @product = ShopifyAPI::Product.find(params[:id])

  response = HTTParty.get(
    "https://#{@shop_session.domain}/admin/metafields.json?metafield[owner_id]=#{@product.id}&metafield[owner_resource]=product",
    headers: { "X-Shopify-Access-Token" => @shop_session.token }
  )

  @metafields = response.parsed_response["metafields"].map do |metafield|
    OpenStruct.new(metafield)
  end
end

Create a view for Product page

<h2 class="mb-4">
  <%= @product.title %>
  <img src="<%= @product.image.src %>" class="img-thumbnail float-right small-img-thumbnail" />

  <div class="float-right">
    <%= link_to 'New Metafield', new_metafield_path(owner_id: @product.id, owner_class: 'Product'), class: 'btn btn-outline-primary' %>
  </div>
</h2>

<table class="table">
  <thead class="thead-light">
    <tr>
      <th scope="col">#</th>
      <th scope="col">Namespace</th>
      <th scope="col">Key</th>
      <th scope="col">Type</th>
      <th scope="col">Value</th>
      <th scope="col">Actions</th>
    </tr>
  </thead>

  <tbody>
    <% @metafields.each_with_index do |metafield, index| %>
      <tr>
        <th scope="row"><%= index + 1 %></th>
        <td><%= metafield.namespace %></td>
        <td><%= metafield.key %></td>
        <td><%= metafield.value_type %></td>
        <td><%= metafield.value %></td>
        <td>
          <%= link_to 'Edit', "#", class: 'btn btn-link' %>

          <%= link_to 'Delete', "#", method: :delete, data: {confirm: 'Are you sure?'}, class: 'btn btn-link' %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>
← Back to Blog