Shopify is an e-commerce platform that allows you to easily and quickly create, configure and run your own online store. In accordance with Shopify, more than 1,000,000 merchants use Shopify around the world.
Shopify provides API endpoints to interact with data about individual Shopify stores, data such as products, orders, customers and more.
Let's see how we can implement CRUD functionality with products in a Nativescript Vue app using the Shopify Admin API.
Give Oscar Lira a follow!
Private apps are built exclusively for individual Shopify stores. You can use them to access data directly using Shopify's APIs or extend online stores to other platforms; in our case mobile apps.
So we need to go to the Shopify store and create a private app in order to generate the Api Key we're gonna use in our Nativescript app and provide Read and Write permissions for the products as well. (if you don't know how to create a private app you can visit this link)
Once we have the Api Key and the permissions for the products in Shopify, we create the Nativescript project with Vue, in this case I use Typescript, but if you want to use plain JavaScript, no problem!! the scaffolding of this app would be the same
vue init nativescript-vue/vue-cli-template shopify-nativescript
npm install
Remember to have Nativescript up to date to use the latest version of it
Now go into the app folder and add the followin packages:
"@nstudio/nativescript-cardview": "^2.0.1"
"@nstudio/nativescript-floatingactionbutton": "^3.0.4"
"nativescript-toast": "^2.0.0"
"vue-class-component": "^7.2.6",
"vue-property-decorator": "^9.1.2"
First we need the URL of the Shopify store and the base64 String formed by the Api Key and Password.
Follow the next two steps to generate it
Join the API key and password with a single colon (
:
)Encode the resulting string in base64 representation
You can get more info here
Then create a ShopifyService class where will contain all the requests to the Shopify Admin API
import { Http } from "@nativescript/core";
export default class ShopifyService {
static STOREURL:string = "https://nativescript-store-1.myshopify.com/"
static BASIC_AUTHENTICATION:string = "YOUR_BASE64"
}
In the ShopifyService Class add the viewProduct function to retrieve the products of the store...
🧐 Notice we're requesting the endpoint
admin/api/2021-01/products.json
and adding the basic Authentication (base64) in the header of the request as well.
Here is the complete list of Shopify endpoints https://shopify.dev/docs/admin-api/rest/reference
static ViewProducts() {
return Http.request({
url: this.STOREURL + 'admin/api/2021-01/products.json',
method: 'GET',
headers: {
'Authorization': 'Basic ' + this.BASIC_AUTHENTICATION,
"Content-Type": "application/json"
},
});
}
Our main App.vue view is going to have a ListView and a Floating Action Button to add products:
<Page>
<ActionBar title="Shopify API in NativeScript-Vue!" />
<GridLayout columns="*" rows="*">
<ActivityIndicator row="0" :busy="loading"
:visibility="!loading ? 'collapse' : 'visible'" verticalAlignment="top"/>
<ListView for="item in products" row="0">
<v-template>
<CardProduct :p_product="item" />
</v-template>
</ListView>
<Fab @tap="add" row="0" rippleColor="#f1f1f1" class="fab-button fa">
{{"fa-plus" | fonticon}}
</Fab>
</GridLayout>
</Page>
and it looks like this 😎😎😎
The code for the App.vue contains the mounted hook where will dispatch the load action to call the Shopify products via Vuex
I use Vuex Modules and I have a separate file for actions, getters, mutation-types, mutation and modules
😬 No worries! at the end of the post I share the complete code in Git Hub
Remember that the mounted function executes when the view is loaded
<script lang="ts">
...
export default class App extends Vue {
mounted() {
let vm = this;
vm.loading = true;
this.$store.dispatch("shopifyProducts/load").then(function () {
vm.loading = false;
});
}
...
}
This is the load action in Vuex, here it calls the viewProducts function, previously created in the ShopifyService class and adds each product in an array via a mutation
export const load = ({ commit }) => {
return new Promise((resolve, reject) => {
ShopifyService.ViewProducts().then(function (data) {
let content = JSON.parse(data.content+"");
content.products.forEach(p => {
commit(types.ADD, {
id: p.id,
title: p.title,
body_html: p.body_html == null ? '':p.body_html
})
});
resolve(true)
});
});
}
🧐 Notice we have a CardProduct.vue component in the main App.vue inside the ListView element
<card-view margin="5" elevation="10" radius="1">
<StackLayout>
<label :text="p_product.title" class="h2"></label>
<Image :src="imgSrc" stretch="aspectFit" />
<HtmlView :html="p_product.body_html" />
<StackLayout class="m-10 hr"></StackLayout>
<StackLayout orientation="horizontal">
<Label @tap="delete_" horizontalAlignment="right" class="text-danger h3 fa"
style="margin-left: 0">{{ "fa-trash-o" | fonticon }} Delete</Label>
<Label @tap="edit" horizontalAlignment="right" class="text-primary h3 fa">
{{ "fa-pencil-square-o" | fonticon }} Edit</Label>
</StackLayout>
</StackLayout>
</card-view>
This component will show each product in the ListView receiving a product as a prop to show the title and description of the product, but what about the image?
... Its corresponding code will dispatch the loadImageByProduct action via Vuex to get the images of the products inside the mounted hook
...
@Prop() private p_product: {
id: "";
title: "";
body_html: "";
};
...
mounted() {
let vm = this;
store.dispatch("shopifyProducts/loadImageByProduct", this.p_product)
.then(function (img) {
vm.imgSrc = img;
})
}
...
In the ShopifyService class we have the function which gets the images of the product with the URL admin/api/2021-01/products/${product_id}/images.json
. This function receives the product_id argument to identify the product in Shopify
static loadImageByProduct(product_id) {
return Http.request({
url: this.STOREURL + `admin/api/2021-01/products/${product_id}/images.json`,
method: 'GET',
headers: {
'Authorization': 'Basic ' + this.BASIC_AUTHENTICATION,
"Content-Type": "application/json"
},
});
}
Now add the function which creates the product, here we use the admin/api/2021-01/products.json
endpoint and receives the product object composed of only the name of the product
static addProduct(data) {
let product = {
product:data
}
return Http.request({
url: this.STOREURL + `admin/api/2021-01/products.json`,
method: 'POST',
headers: {
'Authorization': 'Basic ' + this.BASIC_AUTHENTICATION,
"Content-Type": "application/json"
},
content: JSON.stringify(product)
});
}
In Vuex add the corresponding addProductTitle action which calls the addProduct function of the ShopifyService class
In this case to keep it simple I create the product with only the name of product
This action creates the product, in turn, adds each product in an array via a mutation
export const addProductTitle = ({ commit }, data) => {
return new Promise((resolve, reject) => {
let product = data;
ShopifyService.addProduct(product).then(function (data) {
let content = JSON.parse(data.content+"");
if(data.statusCode == 201){
commit(types.ADD, {
id:content.product.id,
title:content.product.title,
body_html: content.body_html == null ? '':content.body_html
})
resolve(true)
}
else
reject('error')
});
});
}
In the main App.vue we add the function to create a product when tapping the floating action button
...
add(){
let vm = this;
prompt("Add product").then((result) => {
if (result.result) {
this.$store.dispatch("shopifyProducts/addProductTittle", {
title: result.text,
}).then(function (data) {
var toast = Toast.makeText("Added successfully");
toast.show();
}).catch(function (data) {
var toast = Toast.makeText("Error");
toast.show();
});
}
});
}
...
In the CardProduct.vue component we add the last two functions they are edit and delete functions
...
edit() {
let vm = this;
prompt("Edit title of the product", this.p_product.title).then((result) => {
if (result.result) {
store.dispatch("shopifyProducts/updateTitleProduct", {
id: vm.p_product.id,
title: result.text,
})
.then(function (data) {
var toast = Toast.makeText("Updated successfully");
toast.show();
}).catch(function (data) {
var toast = Toast.makeText("Error");
toast.show();
});
}
});
}
delete_() {
let vm = this;
confirm("Delete?").then((result) => {
if (result) {
store.dispatch("shopifyProducts/deleteProduct", {
id: vm.p_product.id,
}).then(function (data) {
var toast = Toast.makeText("Deleted successfully");
toast.show();
}).catch(function (data) {
var toast = Toast.makeText("Error");
toast.show();
});
}
});
}
...
...And for Vuex we have its corresponding actions which call addProduct and deleteProduct functions of the ShopifyService class
export const addProductTittle = ({ commit }, data) => {
return new Promise((resolve, reject) => {
let product = data;
ShopifyService.addProduct(product).then(function (data) {
let content = JSON.parse(data.content+"");
if(data.statusCode == 201){
commit(types.ADD, {
id:content.product.id,
title:content.product.title,
body_html: content.body_html == null ? '':content.body_html
})
resolve(true)
}
else
reject('error')
});
});
}
export const deleteProduct = ({ commit }, data) => {
return new Promise((resolve, reject) => {
let product = data;
ShopifyService.deleteProduct(product.id).then(function (data) {
if(data.statusCode == 200){
commit(types.DELETE, product)
resolve(true)
}
else
reject('error')
});
});
}
Here we have the said functions in the ShopifyService class
export default class ShopifyService {
...
static updateTitleProduct(product_id, data) {
let product = {
product:data
}
return Http.request({
url: this.STOREURL + `admin/api/2021-01/products/${product_id}.json`,
method: 'PUT',
headers: {
'Authorization': 'Basic ' + this.BASIC_AUTHENTICATION,
"Content-Type": "application/json"
},
content: JSON.stringify(product)
});
}
static deleteProduct(product_id) {
return Http.request({
url: this.STOREURL + `admin/api/2021-01/products/${product_id}.json`,
method: 'DELETE',
headers: {
'Authorization': 'Basic ' + this.BASIC_AUTHENTICATION,
"Content-Type": "application/json"
},
});
}
...
Shopify is a complete e-commerce platform however it provides APIs to create new functionality. There are a lot of ecommerce needs that you can solve by taking advantage of the Shopify APIs. For example, imagine developing customized behavior around updating products, inventory, customers or even receiving order notifications... but all that in a mobile application.
This was just a simple example about what you can do with Shopify and Nativescript! 💪😄
You can check out the code here