Home > Uncategorized > Dissecting the #Shopify #NodeJs Bridge

Dissecting the #Shopify #NodeJs Bridge

maxresdefault

The Shopify NodeJs bridge by MobileFront is a NodeJS API that runs on Heroku, and it allows easy access to your Shopify Data via a JSON based API.

Here’s the core of the code in index.js

(function () {

‘use strict’;

var express = require(‘express’),
sprintf = require(‘sprintf’),
crypto = require(‘crypto’),
fs = require(‘fs’),
toSlugCase = require(‘to-slug-case’),
httpRequest = require(‘./http.request’),
app = express(),
shopifyAPI = require(‘shopify-node-api’),
port = process.env.PORT || 3000,
config = JSON.parse(fs.readFileSync(‘config.json’, ‘utf8’)),
shopify = new shopifyAPI(config);
// Shopify App install

app.get(‘/install’, function (req, res) {
//res.send(‘This is ShopToApp installation page’);
var shop = req.query.shop,
apiKey = req.query.apiKey,
apiSecret = req.query.apiSecret,
Shopify,
authURL;

if (config.access_token) {
// reject, we should not allow installing the app more than once
res.status(400).send({
status: ‘failed’,
message: ‘The app is already installed’
});

} else if (!shop || !apiKey || !apiSecret) {
res.status(400).send({
status: ‘failed’,
message: ‘Missing required parameters’
});
} else {
config.shop = shop;
config.shopify_api_key = apiKey;
config.shopify_shared_secret = apiSecret;
config.redirect_uri = req.headers[“x-forwarded-proto”] + ‘://’ + req.get(‘host’) + ‘/auth’;
config.nonce = crypto.randomBytes(64).toString(‘hex’);

Shopify = new shopifyAPI(config);
authURL = Shopify.buildAuthURL();

res.redirect(authURL);
}

});

app.get(‘/auth’, function (req, res) {

//params: code, hmac, timestamp, state, shop

var requestParams = req.query,
shop,
Shopify;

if (req.query.shop) {
shop = req.query.shop.replace(‘.myshopify.com’, ”);
config.shop = shop;
Shopify = new shopifyAPI(config);

Shopify.exchange_temporary_token(requestParams, function (err, data) {
if (!err) {

config.access_token = data.access_token;
shopify = new shopifyAPI(config);

res.status(200).send(‘You have successfully installed the MobileFront Bridge! Your Access Token is: ‘ + config.access_token + ‘. Keep it safe.’);
} else {
res.status(400).send({
status: ‘failed’,
message: ‘Failed to install the app. Reason: ‘ + err + ‘, shop: ‘ + config.shop
});
}
});
} else {
res.status(400).send({
status: ‘failed’,
message: ‘Invalid parameters’
});
}

});

// API

// =======================
// CORS ==================
// =======================
app.use(‘/’, function (req, res, next) {
res.header(‘Access-Control-Allow-Origin’, ‘*’);
res.header(‘Access-Control-Allow-Methods’, ‘GET,PUT,POST,DELETE,OPTIONS’);
res.header(‘Access-Control-Allow-Headers’, ‘Content-Type, Authorization, Content-Length, X-Requested-With’);
// intercept OPTIONS method
if (‘OPTIONS’ === req.method) {
res.sendStatus(200);
} else {
next();
}
});

app.use(‘/api’, function (req, res, next) {
if (!config.shop || !config.access_token) {
res.status(400).send({
status: ‘failed’,
message: ‘Bridge was not installed’
});
} else {
req.shopify = shopify;
next();
}

});

app.get(‘/api/getShopData’, function (req, res) {
var shopify = req.shopify;

shopify.get(‘/admin/shop.json’, function (err, data, headers) {
if (!err) {
res.json(data);
} else {
res.status(400).json({
status: ‘failed’,
message: err
});
}
});
});

app.get(‘/api/getSmartCollections’, function (req, res) {
var shopify = req.shopify;

shopify.get(‘/admin/smart_collections.json’, function (err, data, headers) {
if (!err) {
res.json(data);
} else {
res.status(400).json({
status: ‘failed’,
message: err
});
}
});
});

app.get(‘/api/getCollection’, function (req, res) {
var shopify = req.shopify,
collectionId = req.query[‘collectionId’];

if (collectionId) {
// first look in smart collections
shopify.get(‘/admin/smart_collections/’ + collectionId + ‘.json’, function (err, data, headers) {
if (!err) {
res.json(data.smart_collection);
} else {
// if not found look in custom collections
shopify.get(‘/admin/custom_collections/’ + collectionId + ‘.json’, function (err, data, headers) {
if (!err) {
res.json(data.custom_collection);
} else {
res.status(400).json({
status: ‘failed’,
message: err
});
}
});
}
});
} else {
res.status(400).json({
status: ‘failed’,
message: ‘Missing or empty collectionId parameter’
});
}
});
app.get(‘/api/getProductsForCollection/’, function (req, res) {
var shopify = req.shopify,
query = req.query,
params = Object.keys(query).map(function (key) {
var keyValue = key + ‘=’;
if (key === ‘title’) {
keyValue += encodeURIComponent(query[key]);
} else {
keyValue += query[key];
}
return keyValue;
});

shopify.get(‘/admin/products.json?’ + params.join(‘&’), function (err, data, headers) {
if (!err) {
res.json(data);
} else {
res.status(400).json({
status: ‘failed’,
message: err
});
}
});
});

app.get(‘/api/getNavigationLinks’, function (req, res) {
var SHOPIFY_STORE_URL = ‘https://’ + config.shop + ‘.myshopify.com’,
url = SHOPIFY_STORE_URL + ‘/apps/proxy/categories.json’;

httpRequest.get(url, null, null, function (result) {
res.json(result);
}, function (error) {
console.log(‘/api/getNavigationLinks: ‘ + JSON.stringify(error));
res.status(400).json({
status: ‘failed’,
message: ‘Failed to retrieve navigation links. Is your shop password protected?’
});
});
});

// the route below is used by Shopify when processing proxy requests
app.get(‘/proxy/categories.json’, function (req, res) {
var parentCategory = req.query.parent || ‘main-menu’,
output;

//output = ‘{% layout none %}{% capture items %}{% for link in linklists.’ + parentCategory + ‘.links %}{% if link.type == “collection_link” %}{ “title”: “{{link.title}}”, “handle”: “{{link.title | handleize}}”, “collection”: “{{link.object.id}}”},{% endif %}{% endfor %}{% endcapture items %}{% assign length = items | size %}{% if length != 0 %}{% assign sliceLength = length | minus: 1 %}{% assign items = items | slice : 0, sliceLength %}{% endif %}[{{items}}]’;
output = ‘{% layout none %}{% capture items %}{% for linklist in linklists %}{% for link in linklist.links %}{% if link.type == “collection_link” %}{ “title”: “{{link.title}}”, “handle”: “{{link.title | handleize}}”, “collection”: “{{link.object.id}}”, “parent”: “{{linklist.handle}}”},{% endif %}{% endfor %}{% endfor %}{% endcapture items %}{% assign length = items | size %}{% if length != 0 %}{% assign sliceLength = length | minus: 1 %}{% assign items = items | slice : 0, sliceLength %}{% endif %}[{{items}}]’;

res.set(‘Content-Type’, ‘application/liquid’);
res.send(output);
});

app.listen(port);
})();

I have this running on a development shop, with one product, so here is what it returns for it’s various endpoints;

http://hidden-bastion-72179.herokuapp.com/api/getNavigationLinks

[
{
“title”: “Gym”,
“handle”: “gym”,
“collection”: “422347917”,
“parent”: “main-menu”
}
]

http://hidden-bastion-72179.herokuapp.com/api/getShopData

{
“shop”: {
“id”: 18854285,
“name”: “webtropy”,
“email”: “fiach.reid@gmail.com”,
“domain”: “webtropy.myshopify.com”,
“created_at”: “2017-03-29T05:04:07-04:00”,
“province”: “”,
“country”: “GB”,
“address1”: “10 Nualamont Drive, 10 Nualamont Drive”,
“zip”: “BT48 9PH”,
“city”: “Derry”,
“source”: null,
“phone”: “”,
“updated_at”: “2017-03-29T05:25:02-04:00”,
“customer_email”: null,
“latitude”: null,
“longitude”: null,
“primary_location_id”: 40035597,
“primary_locale”: “en”,
“address2”: null,
“country_code”: “GB”,
“country_name”: “United Kingdom”,
“currency”: “GBP”,
“timezone”: “(GMT-05:00) Eastern Time (US & Canada)”,
“iana_timezone”: “America\/New_York”,
“shop_owner”: “Fiach Reid”,
“money_format”: “\u00a3{{amount}}”,
“money_with_currency_format”: “\u00a3{{amount}} GBP”,
“weight_unit”: “lb”,
“province_code”: null,
“taxes_included”: true,
“tax_shipping”: null,
“county_taxes”: true,
“plan_display_name”: “affiliate”,
“plan_name”: “affiliate”,
“has_discounts”: false,
“has_gift_cards”: false,
“myshopify_domain”: “webtropy.myshopify.com”,
“google_apps_domain”: null,
“google_apps_login_enabled”: null,
“money_in_emails_format”: “\u00a3{{amount}}”,
“money_with_currency_in_emails_format”: “\u00a3{{amount}} GBP”,
“eligible_for_payments”: true,
“requires_extra_payments_agreement”: false,
“password_enabled”: false,
“has_storefront”: true,
“eligible_for_card_reader_giveaway”: false,
“finances”: true,
“setup_required”: false,
“force_ssl”: true
}
}

http://hidden-bastion-72179.herokuapp.com/api/getSmartCollections

{
“smart_collections”: [
{
“id”: 422347917,
“handle”: “gym”,
“title”: “Gym”,
“updated_at”: “2017-03-29T05:40:56-04:00”,
“body_html”: “”,
“published_at”: “2017-03-29T05:38:00-04:00”,
“sort_order”: “best-selling”,
“template_suffix”: null,
“published_scope”: “global”,
“disjunctive”: false,
“rules”: [
{
“column”: “tag”,
“relation”: “equals”,
“condition”: “Gym”
}
]
}
]
}

http://hidden-bastion-72179.herokuapp.com/api/getCollection?collectionId=422347917

{
“id”: 422347917,
“handle”: “gym”,
“title”: “Gym”,
“updated_at”: “2017-03-29T05:40:56-04:00”,
“body_html”: “”,
“published_at”: “2017-03-29T05:38:00-04:00”,
“sort_order”: “best-selling”,
“template_suffix”: null,
“products_count”: 1,
“published_scope”: “global”,
“disjunctive”: false,
“rules”: [
{
“column”: “tag”,
“relation”: “equals”,
“condition”: “Gym”
}
]
}

http://hidden-bastion-72179.herokuapp.com/api/getProductsForCollection

{
“products”: [
{
“id”: 9487359757,
“title”: “Dumbells”,
“body_html”: “”,
“vendor”: “webtropy”,
“product_type”: “”,
“created_at”: “2017-03-29T05:40:56-04:00”,
“handle”: “dumbells”,
“updated_at”: “2017-03-29T05:40:58-04:00”,
“published_at”: “2017-03-29T05:39:00-04:00”,
“template_suffix”: null,
“published_scope”: “global”,
“tags”: “gym”,
“variants”: [
{
“id”: 35073137037,
“product_id”: 9487359757,
“title”: “Default Title”,
“price”: “100.00”,
“sku”: “”,
“position”: 1,
“grams”: 0,
“inventory_policy”: “deny”,
“compare_at_price”: null,
“fulfillment_service”: “manual”,
“inventory_management”: null,
“option1”: “Default Title”,
“option2”: null,
“option3”: null,
“created_at”: “2017-03-29T05:40:56-04:00”,
“updated_at”: “2017-03-29T05:40:56-04:00”,
“taxable”: true,
“barcode”: “”,
“image_id”: null,
“inventory_quantity”: 1,
“weight”: 0,
“weight_unit”: “lb”,
“old_inventory_quantity”: 1,
“requires_shipping”: true
}
],
“options”: [
{
“id”: 11375442061,
“product_id”: 9487359757,
“name”: “Title”,
“position”: 1,
“values”: [
“Default Title”
]
}
],
“images”: [
{
“id”: 22923954125,
“product_id”: 9487359757,
“position”: 1,
“created_at”: “2017-03-29T05:40:58-04:00”,
“updated_at”: “2017-03-29T05:40:58-04:00”,
“src”: “https:\/\/cdn.shopify.com\/s\/files\/1\/1885\/4285\/products\/exercise-equipment-product-image.jpg?v=1490780458”,
“variant_ids”: [

]
}
],
“image”: {
“id”: 22923954125,
“product_id”: 9487359757,
“position”: 1,
“created_at”: “2017-03-29T05:40:58-04:00”,
“updated_at”: “2017-03-29T05:40:58-04:00”,
“src”: “https:\/\/cdn.shopify.com\/s\/files\/1\/1885\/4285\/products\/exercise-equipment-product-image.jpg?v=1490780458”,
“variant_ids”: [

]
}
}
]
}

Advertisement
Categories: Uncategorized
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: