Modern theme development in Shopify, Part One
thoughts from lunchtime labs
Life without MVC
Shopify theme development does not allow access to the Model or the Controller layers like a traditional MVC structured repository. These are places where developers would traditionally store logic and computation before delivering pre-processed data to the presentation layer.
As such, we often see Liquid code strewn with complex logic which is difficult to read, modify, fix, or extend.
We often hedge against this by moving to client-side MVC with a framework such as Vue.JS, which allows us to develop with a proper separation of concerns.
Environment variables
Environment variables (or the lack thereof) are one of the challenges created by
view-only development that Shopify theme development insists on. Liquid does not
allow ENV
variables, nor does it not easily lend itself to server-side
solutions that would mimic that sort of functionaltiy, rendering adherence to
the principles of a proper 12 factor app nearly
impossible.
In this unit, we elect to store ENV
-like variables in a window
level
(global) JavaScript, object, rather than Liquid
. This gives us the ability to
generate modular, well-written code.
A valid concern is that it may be improper to store ENV
variables in the front
end code. Because client-side code is inherently open-source and insecure, it’s
important not to accidentally store any secrets in the JS
environment.
Luckily, we aren’t going to find a lot of private API keys in the Liquid code
anyway. Because Liquid
is already a view layer, it’s not able to perform
server-to-server calls. There are already few secrets in Liquid
code (with
some exceptions, i.e. values that are hashed before being surfaced).
One reason you might wish to add ENV
variables is if your Shopify theme relies
on custom apps. In this case, you would want your staging
store to access
the corresponding assets on your staging
app so that you can properly run
acceptance.
We put configuration right into the JavaScript:
# assets/js/env/index.js
let config = {
development: {
store_env: 'development',
my_domain: 'https://custom-domain.ngrok.io',
},
production: {
store_env: 'production',
my_domain: 'https://my-app.herokuapp.com'
},
staging: {
store_env: 'staging',
my_domain: 'https://my-app-staging.herokuapp.com'
},
shared: {
my_path_to_js: "/assets/my_asset.js"
},
}
window.ENV = {
shop_env: function() {
let domain = window.location.hostname;
if (domain.indexOf("staging") > -1) {
return config.staging.store_env
} else if (domain.indexOf("dev") > -1) {
return config.development.store_env
} else {
return config.production.store_env
}
},
my_domain: function() {
return config[ENV.shop_env()].my_domain
},
my_js_url: function() {
return ENV.my_domain() + config.shared.my_path_to_js
}
}
Ouala. We can access the variables from our theme code as follows:
# snippets/mysnippet.liquid
$.getScript({
url: ENV.my_js_url,
})