When working with Shopify apps it’s usually easy to have multiple environments. We simply create a separate development store for development, staging, product, and deploy a dedicated app instance for each. But how we can use multiple environments for theme app extensions?
With app extensions, things are a bit more tricky. Every extension is attached to a Shopify app and has its own set of credentials: ID and UUID. App extension is usually located in theme-app-extension
folder in your app and we use the Shopify CLI command to upload it to Shopify: shopify extension push
. When we create an app extension it generates theme-app-extension/.env
file that contains credentials for both Shopify app and extension. Without this file, we won’t be able to upload an extension. And as you may guess it will only have credentials for one app/extension at a time.
With this in mind, how we can push extension to multiple environments (eg development & production)? Well, only by editing theme-app-extension/.env
and putting there correct credentials for the current environment. Sounds not great, yeah? Doing this manually is the simplest approach you can take. But let’s take a look at how we can automate this.
Here’s the approach that I use:
- Save API keys for each app environment in encrypted credentials files.
- Create config for app extension credentials for each environment.
- Setup CLI commands to automatically generate
theme-app-extension/.env
before runningshopify extension push
command and delete afterward.
Below I will show the implementation for Ruby on Rails. For Node the approach will be the same but you will need to figure out the implementation yourself.
Step 1. App credentials
By default apps generated with shopify_app
gem store API keys in .env
file (an example). We need to change that and switch to built-in Rails credentials. This will allow us to securely store credentials for multiple environments and easily access production API keys when we need to publish app extension to production.
Run the following command to open credentials file:
bin/rails credentials:edit
And put your development API keys in the following format:
shopify:
api_key: KEY
api_secret: SECRET
Close the file to save and encrypt credentials.
Do the same for production environment:
bin/rails credentials:edit -e production
And update Shopify config to use credentials:
# config/initializers/shopify_app.rb
ShopifyApp.configure do |config|
# ...
config.api_key = Rails.application.credentials.dig(:shopify, :api_key)
config.secret = Rails.application.credentials.dig(:shopify, :api_secret)
end
For more details on Rails credentials read the official doc.
Step 2. App extension credentials
With extension credentials, everything is simpler. They don’t contain sensitive data so we can skip encryption.
Create config file:
# config/theme_extension.yml
development:
title: Extension Name
id: EXT_ID
uuid: EXT_UUID
test:
title: Extension Name
id: EXT_ID
uuid: EXT_UUID
production:
title: Extension Name
id: EXT_ID
uuid: EXT_UUID
And setup config reference:
# config/application.rb
module AppName
class Application < Rails::Application
# ...
# Theme Extension
config.theme_extension = config_for(:theme_extension)
end
end
Now we can read config like this:
Rails.application.config_for(:theme_extension)
Step 3. Rake tasks
Create the following rake file:
# lib/tasks/extension.rake
namespace :extension do
desc "Show theme extension info for current environment"
task info: :environment do
with_dot_env do
system("cd theme-app-extension; bundle exec shopify extension info")
end
end
desc "Push theme extension for current environment"
task push: :environment do
with_dot_env do
system("cd theme-app-extension; bundle exec shopify extension push")
end
end
def env_vars
config = Rails.application.config_for(:theme_extension)
{
"SHOPIFY_API_KEY" => Rails.application.credentials.shopify.fetch(:api_key),
"SHOPIFY_API_SECRET" => Rails.application.credentials.shopify.fetch(:api_secret),
"EXTENSION_TITLE" => config[:title],
"EXTENSION_ID" => config[:id],
"EXTENSION_UUID" => config[:uuid],
}
end
def dot_env_path
Rails.root.join("theme-app-extension/.env")
end
def create_dot_env
File.open(dot_env_path, "w") do |file|
env_vars.each do |key, value|
file.write "#{key}=#{value}\n"
end
end
end
def delete_dot_env
File.delete(dot_env_path) if File.exists?(dot_env_path)
end
def with_dot_env
create_dot_env
yield
delete_dot_env
end
end
In the code above #with_dot_env
method wraps every Shopify CLI call and manages theme-app-extension/.env
file creation and deletion. This allows us not to worry about credentials.
How to use it
Now we have two rake commands.
To fetch extension info:
bin/rake extension:info
To push extension to Shopify:
bin/rake extension:push
And we can easily switch environment:
RAILS_ENV=production bin/rake extension:info
RAILS_ENV=staging bin/rake extension:push
I think it’s a pretty nice approach to isolate the complexity of environment management. I used it for building my new Product Color Swatches app and it works pretty well.
If you like reading about Shopify app development follow me on Twitter.