-->
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.
There are two types of applications that Shopify provides:
Private Applications: Private apps are built exclusively for your Shopify store. You can use private apps to add features to your Shopify admin, access your store’s data directly using Shopify’s APIs, or extend your online store to other platforms using custom storefronts.
Public Applications: A public Shopify app can interact with the Shopify API on behalf of multiple stores. To authenticate with Shopify using a public app, you’ll need to generate the credentials from your Partner Dashboard and then use them to implement OAuth.
In this post, I will talk about public Shopify applications.
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
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.
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
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
.
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.
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.
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
From your Shopify Partner account
, you will see an option to create a store. From there, create a development store for testing purposes.
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.
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
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>