Storing Shopify App Data in Metafields
When developing a Shopify app, it's important to determine the best strategy for storing and retrieving app data within a theme. The decision of where to store a Shopify app's data will influence the overall data management strategy for an app.
In this post, I'll give an overview of two common approaches for storing and retrieving Shopify app data within a theme. I'll also share an example of how my upcoming Best Seller Insights app will be storing and displaying best-seller rankings on each best-selling product page.
Shopify app data storage options
Store data externally
Storing Shopify app data externally means the data would live outside of the Shopify ecosystem. For example, the data may live in another database or other storage mechanism that is hosted outside of Shopify.
Each Shopify theme page that needs access to this external data would need to fetch the data from the external source using Javascript.
The data would then need to be injected into the theme's page using Javascript.
Pros:
- full control of the data
Cons:
- data can't be handled directly within the theme via Liquid
- external calls for the data will be slower
- an external dependency for the data means a higher risk of downtime for portions of your shop that rely on the external data
Store data in Shopify Metafields
Shopify provides support for storing custom data directly within Shopify via Metafields. A Shopify Metafield consists of:
key
- the key / name of the metafieldvalue
- the value of the metafieldvalue_type
- eitherstring
orinteger
namespace
- a namespace to group metafields together for easier management, and to avoid naming collisions
Shopify apps can use Metafields to store data directly on many objects within the Shopify ecosystem including: products, orders and collections.
Shopify provides direct support for accessing Metafield within Liquid, or Javascript may also be used.
Pros:
- data can be accessed directly from Liquid
- everything stays within the Shopify ecosystem - no external server requests are needed
Cons:
- data management is a bit harder; multiple additional Shopify API calls are required for updating metafields
- complex data support is limited due to the
string
andinteger
limitations
Why I chose the Metafield approach for my app
For my Best Seller Insights app, I decided to go with the Metafield approach.
The Liquid template support makes it easier to work with the data directly, and as a result should make it easier for Shopify stores to customize the rendering as needed.
Another big motivator for me is the lack of an external dependency. Since the data lives 100% within the Shopify ecosystem, my app will not be able to negatively impact a store if my server experiences any downtime. In addition, the overall rendering time for the data should be much faster since it will not require external network requests.
Example: Storing Shopify app data in a product metafield
My Best Seller Insights app will track a product's best-seller rank across one or more best-seller collections. For each product, there will be a list of collections, and the products sales rank within that collection.
Rather than storing the collection ranks in separate individual Metafields, I decided to combine them together into one field to make them easier to work with. Initially, I was planning on using JSON to store the data, since it provides a great way to structure data in a string format. However, I then discovered it is currently not possible to parse JSON from Liquid within Shopify. There is an open issue requesting JSON parse support, but it is going on 2 years old, so I moved on to plan B.
Plan B involves creating my own format to store the data together. The end result is shown here:
{
"id": 12345,
"namespace": "bsi",
"key": "best-seller-ranks",
"value": "1:best-selling-costumes;1:ghost-apparel;2:mens-costumes",
"value_type": "string",
"description": null,
"owner_id": 12345,
"created_at": "2016-03-03T10:31:41-05:00",
"updated_at": "2016-03-03T12:47:36-05:00",
"owner_resource": "product"
}
As you can see:
- The metafield is stored under namespace
bsi
, and keybest-seller-ranks
. Thebsi
namespace should hopefully avoid collisions with other apps - The
value
is a list of ranks and collection handles - Each group of rank:collection is separated by a semicolon
;
- Each rank / collection pair is separated by a colon
:
Since the rank and collection handle will not contain either a colon or semicolon, these are safe choices to use to separate the sections of this field.
We can then use the bsi.best-seller-ranks
Metafield directly from Liquid like so:
{% assign bsi_ranks = product.metafields.bsi.best-seller-ranks | split: ';' %}
{% unless bsi_ranks.empty? %}
<div>
<ul style="list-style-type: none">
{% for rank in bsi_ranks %}
{% assign bsi_rank = rank | split: ':' %}
{% assign bsi_collection = collections[bsi_rank[1]] %}
{% unless bsi_collection.empty? %}
<li style="">#{{ bsi_rank[0] }} Best Seller in <a href="{{ bsi_collection.url }}">{{ bsi_collection.title }}</a></li>
{% endunless %}
{% endfor %}
</div>
{% endunless %}
A few highlights:
- The Metafield is looked up via the
product.metafields.bsi.best-seller-ranks
call - The rank / collection groups are split using the Liquid split filter on
;
- The individual rank / collection pairs are then split again on
:
- The collection is then retrieved by its handle on line 7:
collections[bsi_rank[1]]
- Once we have the collection, we use it to build links to the collection with the ranking.
Here is the final result:
Summary
In this post, I've covered two common ways for storing and retrieving Shopify app data:
- using an external source and Javascript
- using Shopify Metafields and either Liquid or Javascript
Each of these techniques has their own pros and cons. I opted for the Metafield approach for my upcoming Best Seller Insights app since the pros outweighed the cons in my case.
However, for some Shopify apps, such as those with more complex data requirements, the external source and Javascript approach may be a better option.