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.
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:
Cons:
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
- either string
or integer
namespace
- a namespace to group metafields together for easier management, and to avoid naming collisionsShopify 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:
Cons:
string
and integer
limitationsFor 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.
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:
1
2
3
4
5
6
7
8
9
10
11
12
{
"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:
bsi
, and key best-seller-ranks
. The bsi
namespace should hopefully avoid collisions with other appsvalue
is a list of ranks and collection handles;
:
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
{% 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:
product.metafields.bsi.best-seller-ranks
call;
:
collections[bsi_rank[1]]
Here is the final result:
In this post, I’ve covered two common ways for storing and retrieving Shopify app data:
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.