In this article, we’ll see through a simple example how to develop a Shopify application using Ruby on Rails. Our application will retrieve and display the list of orders passed on a shop You’ll notice that the architecture of a Shopify application is very different from that of a Magento or Prestashop module. The code of a Magento module is directly added to the Magento source code and the module has direct access to the Magento database. A Shopify application on the other hand is hosted on its own server and accesses Shopify data via its API. Hence, the very first step is to configure our Rails application to be able to communicate with the Shopify API.
Create a Shopify partner account
In order to be able to ask for a Shopify API key, you’ll need a Shpoify Partner account. Browse to http://shopify.com/partners and create your account. You’ll then be able to create a new application in order to get an API key and its corresponding secret.
Application configuration
We’ll be using the very useful shopify_app
gem that provides the SessionsController
class as well as code that allows to authentify our application with the Shopify API via Oauth.
Add the gem to your Gemfile
and install your project.
ruby '2.3.3'
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.6'
# Use posgresql as the database for Active Record
gem 'pg'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0', group: :doc
# Handles Shopify API authentication and API requests
gem 'shopify_app', '~> 7.0.0'
# Pagination library for Rails
gem 'will_paginate', '~> 3.1.0'
# I had this gem in a previous version but cant remember why..
# Remove if everything is working fine
# gem 'sinatra'
# Simple, efficient background processing for Ruby
gem 'sidekiq'
We now need to configure the Rails application so that it can communicates with the Shopify API.
I recommend you take advantage of the generator provided by the shopify_app
gem.
The generator will create various files necessary for your application to work properly with Shopify.
rails generate shopify_app --api_key <your_api_key> --secret <your_app_secret>
Among the files created by the generator, we have:
An initialization file
The config/initializer/shopify_app.rb
file contains the configuration allowing your application to authenticate with the Shopify API:
ShopifyApp.configure do |config|
config.api_key = ENV['SHOPIFY_EXPEDITOR_INET_API_KEY']
config.secret = ENV['SHOPIFY_EXPEDITOR_INET_API_SECRET']
config.scope = "read_orders"
config.embedded_app = true
config.webhooks = [
{topic: 'app/uninstalled', address: "#{ENV['WEBHOOK_URL']}app_uninstalled", format: 'json'},
{topic: 'orders/delete', address: "#{ENV['WEBHOOK_URL']}orders_delete", format: 'json'},
{topic: 'orders/create', address: "#{ENV['WEBHOOK_URL']}orders_create", format: 'json'},
{topic: 'orders/updated', address: "#{ENV['WEBHOOK_URL']}orders_updated", format: 'json'}
]
end
In this example, the application requests a read access to the orders of the shop on which the application is installed. We also indicate that the application will be integrated inside the Shopify backend. This means our application will be displayed within an iframe integrated inside the Shopify backend, giving the impression that it is part of Shopify. Finally, the application requests to be notified when certain events regarding orders occur or when the application is deleted from a shop.
The routes configuration file
The config/routes.rb
file has been updated in order to include the ShopifyApp
engine routes inside our application:
Rails.application.routes.draw do
mount ShopifyApp::Engine, at: '/'
root :to => 'orders#index'
end
Besides the routes provided by the engine used during the authentication process with the Shopify API, we add a route to the action that will list existing orders from the shop.
The Shop model
The gem generator creates the Shop
model as well as the associated migration.
The shops on which the application is installed are stored in the shops
table. yor each shop it is installed on, the table also stores the authentication token.
class CreateShops < ActiveRecord::Migration
def self.up
create_table :shops do |t|
t.string :shopify_domain, null: false
t.string :shopify_token, null: false
t.timestamps
end
add_index :shops, :shopify_domain, unique: true
end
def self.down
drop_table :shops
end
end
class Shop < ActiveRecord::Base
include ShopifyApp::Shop
include ShopifyApp::SessionStorage
end
Don’t forget to run your migrations after generating the migration file.
Now that we have turned the Rails application into a Shopify application with the shopify_app
gem, let’s see how we can retrieve the orders list and display it in our application.
The controller
The OrdersController
controller is responsible for retrieving the orders.
It inherits the ShopifyApp::AuthenticatedController
controller provided by the shopify_app
gem.
The get_current_shop
method retrieves the shop the application is currently running on.
The synchronize
method that we’ll study later on retrieves the orders via the Shopify API and stores them in our Rails application’s database.
The models
Besides the Shop
model necessary to any Shopify application, we’ll create the Order
model that represents an order:
class CreateOrders < ActiveRecord::Migration
def change
create_table :orders do |t|
t.string :shopify_order_id, null: false
t.string :shopify_order_name, default: ''
t.datetime :shopify_order_created_at
t.belongs_to :shop, index: true
t.timestamps
end
add_index :orders, :shopify_order_id, unique: true
end
end
class Order < ActiveRecord::Base
belongs_to :shop
end
Finally, let’s look at the Shop
’s model synchronize method that retrieves the orders via the Shopify API and stores them in the database:
class Shop < ActiveRecord::Base
include ShopifyApp::Shop
include ShopifyApp::SessionStorage
has_many :orders, dependent: :destroy
def synchronize
self.orders.delete_all
orders = ShopifyAPI::Order.find(:all, params: {status: :any})
orders.each do |order|
order = Order.create(
{
shopify_order_id: order.id,
shopify_order_name: order.name,
shopify_order_created_at: order.created_at,
})
self.orders << order
end
self.orders_synchronized = true
self.save
end
def orders_synchronized?
return self.orders_synchronized
end
end
The views
We’ll now create the views that will list the orders.
First, let’s have a look at this example layout that takes advantage of the Embedded App SDK that allows to integrate your Rails application directly inside the Shopify administration interface:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Shopify Embedded Example App</title>
<%= stylesheet_link_tag 'application' %>
<%= csrf_meta_tags %>
</head>
<body>
<div class="app-wrapper">
<div class="app-content">
<main role="main">
<%= yield %>
</main>
</div>
</div>
<%= render 'layouts/flash_messages' %>
<script src="https://cdn.shopify.com/s/assets/external/app.js?<%= Time.now.strftime('%Y%m%d%H') %>"></script>
<script type="text/javascript">
ShopifyApp.init({
apiKey: "<%= ShopifyApp.configuration.api_key %>",
shopOrigin: "<%= "https://#{ @shop_session.url }" if @shop_session %>",
debug: <%= Rails.env.development? ? 'true' : 'false' %>,
forceRedirect: true
});
</script>
<%= javascript_include_tag 'application', "data-turbolinks-track" => true %>
<% if content_for?(:javascript) %>
<div id="ContentForJavascript" data-turbolinks-temporary>
<%= yield :javascript %>
</div>
<% end %>
</body>
</html>
The Embedded App SDK allows, among other things, to add buttons, display alerts and modals directly inside the Shopify interface. That is why it needs to authenticate using the Shopify API key.
Below is the view that lists the orders retrieved by the controller:
<% content_for :javascript do %>
<script type="text/javascript">
ShopifyApp.ready(function(){
ShopifyApp.Bar.initialize({
icon: "<%= asset_path('favicon.ico') %>",
pagination: {
previous: <%= (@previous_page.present? ? {href: @previous_page} : nil).to_json.html_safe %>,
next: <%= (@next_page.present? ? {href: @next_page} : nil).to_json.html_safe %>
}
});
});
</script>
<% end %>
<div class="section">
<div class="section-content">
<div class="section-row">
<div class="section-listing">
<div class="section-options">
<ul class="section-tabs">
<li class="active"><a href="#top">All Orders</a></li>
</ul>
<div class="section-content">
<div class="section-row">
<% if @orders.any? %>
<table class="table-section">
<thead>
<tr>
<th class="select-col">
<div class="btn default btn-select-all ico-down">
<input id="select-all" class="checkbox" type="checkbox" value="" name="select-all">
<span class="checkbox-styled"></span>
</div>
</th>
<th class="sortable">Order</th>
<th class="sortable">Date</th>
</tr>
</thead>
<tbody>
<% @orders.each do |order| %>
<tr>
<td>
<input class="checkbox select-order-checkbox" type="checkbox" value="<%= order.id %>">
<span class="checkbox-styled"></span>
</td>
<td><%= link_to order.shopify_order_name, "https://#{@shop_session.url}/admin/orders/#{order.shopify_order_id}", target: "_top" %></td>
<td><%= format_date order.shopify_order_created_at %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div>There is no order.</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Notice that the view adds pagination button using the Embedded App SDK.
Example job
Below is an example job that gets called by the Shopify API when an order is created on a shop, as we asked in the webhooks
instruction in the shopify_app.rb
initialization file.
The webhook system allows to keep our Rails application orders in synchronization with the orders in the shops the application is installed on.
For example, we asked that our application be informed via a webhook whenever a new order is created so that it can create the order in its own database.
Let’s look at the OrdersCreateJob
class:
class OrdersCreateJob < ActiveJob::Base
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)
shop.with_shopify_session do
order =
{
shopify_order_id: webhook[:id],
shopify_order_name: webhook[:order_number],
shopify_order_created_at: webhook[:created_at]
}
order = Order.where(shopify_order_id: order[:shopify_order_id]).first_or_create(order)
shop.orders << order
end
end
end
Last recommendation
I highly recommend that you style your Shopify application using the Shopify Embedded App Frontend Framework frontend library. This library provides CSS and Javascript code allowing you to use the Shopify user interface elements. Your users will be greatly appreciate it as they are already used to this interface.
Conclusion
The example application presented in this article is very limited and doesn’t add any value to what you can do in the Shopify administration interface. However, you now know everything you need to create an awesome Shopify application with Ruby on Rails.