Create product with Shopify Admin API - javascript

I've started working with React and Node.js for the first time, on building an private Shopify App. The app's purpose is to listen to an add to cart button, when clicked it should create a custom product in the Shopify Backend with the Shopify Product API. I've worked plenty in basic JavaScript and have great experience in jQuery, to handle the more basics of this project. The tought part is binding the POST event to the add to cart click, creating the product and adding it to the cart.
I'm trying the following solution for creating the product in the backend. Is this the correct way of doing it, or is there a better solution for doing this?
How can i bind this Fetch function to an click event?
let new_product = {
product: {
title: Custom,
body_html: test,
vendor: Custom,
product_type: customproduct,
tags: Customproduct
}
};
fetch('/admin/api/2020-07/products.json', {
method: 'post',
body: JSON.stringify(new_product),
headers: {
'X-Shopify-Access-Token': process.env.SHOPIFY_API_SECRET_KEY,
'Accept': 'application/json'
},
})
I'm using Isomorphic Fetch in my project, which should work server and client side.
Any help and guidance would be appreciately recieved.
Thank you!

You have got several questions there. Before answering, it is important to clear few misconceptions that I assumed from your wording and sample code. There are 3 types of Shopify Apps.
Public
Custom
Private
So if you are building a Private app, then provided code will not work for creating Product because Private apps use Basic authentication while Public and Custom apps manage authentication with OAuth 2.0.
I'm using Isomorphic Fetch in my project, which should work server and
client side.
Even though it works on Server and Client ends, do not call Shopify API from Client side as this will expose your private app credentials.
To implement what you are trying to do, you need to modify React App as well as Backend code.
Add an event listener to Button
Send a POST request to Backend server
Create Product on Shopify via API
Add the product to cart using ID returned in previous step
Return the Cart URL in response
Sample code in React would look like
function Product() {
const addProduct = async () => {
const cart = await fetch("/your-end-point", {
method: "post",
body: JSON.stringify({
// add product params
}),
});
};
return <button onClick={addProduct}>Add Product</button>;
}
ReactDOM.render(<Product />, document.getElementById("root"));
Then inside your Node.js application, handle the Shopify API part. Instead of using fetch, I will recommend using Official Node.js module for Shopify.
Sample code will look like
const Shopify = require("shopify-api-node");
router.post("/your-end-point", async (req, res) => {
try {
const shopify = new Shopify({
shopName: "your-shop-name",
apiKey: "your-api-key",
password: "your-app-password",
});
const product = await shopify.product.create(req.body);
// use product ID from previous request
const checkout = await shopify.checkout.create({
/*checkout params */
});
res.status(200).send(checkout);
} catch (ex) {
console.log(ex);
}
});

Related

Sending data from frontend to backend in shopify

I'm developing an app with node and react. In this app, I have created a scriptTag using the fetch function to get customer id and product id whenever a specific button is pressed. Now when I have fetched that data, I want to store it in my firebase database, which I cannot import in my script tag file because it's on the front end. Therefore, I need to send the data from my frontend to the backend of the app but I'm confused that if I use the fetch function to make POST and GET requests, what URL should I use? Moreover, can the fetch function even post the variable values or not?
Scripttag.js
const header = $('header.site-header').parent();
header.prepend('<div>Hello this is coming from the public folder </div>').css({'background-color':'orange', 'text-align': 'center'})
function addWishList(customer, productid){
/*
fetch(`url`, {
method: 'POST',
headers: {
'Content-Type': ,
"X-Shopify-Access-Token": ,
},
body:
}})
})
*/
console.log('adding item to the wishlist!!')
}
function removeWishList(){
console.log('removing item from the wishlist!!')
}
var wishbtn = document.querySelector('.wishlist-btn')
wishbtn.addEventListener('click', function(){
if(this.classList.contains('active')){ //condition to check if the button is already pressed
removeWishList();
this.classList.remove('active');
this.innerHTML = 'Add to wishlist'
}
else{
var customer = wishbtn.dataset.customer;
if(customer == null || customer == ''){
console.log('Please log in to add this item to your wishlist')
}
else{
var productid = wishbtn.dataset.product;
this.classList.add('active'); //when the user presses add to wishlist button, it add active to the button's class
this.innerHTML = 'Remove from wishlist'
addWishList(customer, productid);
}
}
})
The best way to send data from the frontend to the backend is to use the Proxy option that is provided in the Shopify Partner Dashboard.
This way you can stay in the Shopify environment since it will pass specific arguments to the request that you can check if the request is truly coming from Shopify and you will have one less worry about securing it from outside requests.
So for example you will make a fetch request to /apps/YOUR_CUSTOM_PATH and this will proxy to your URL https://example.com/custom_path where you will handle the request.
You can see more info here: https://shopify.dev/tutorials/display-data-on-an-online-store-with-an-application-proxy-app-extension
You can create a public route for your app as well that can be requested from everywhere and allow for CORS but it's usually more work to do so... so pick your poison.
Have in mind that when creating a proxy page you need to reinstall the app on the store to take effect.
PS: Don't ever use the Access Token in the frontend, that should be never present in the frontend. The AccessToken is only back-end way of communicating with the API!

Use git credential manager to fetch azure devops api instead of personal access token

I am trying to fetch git azure devops api to get information about repositories and branches in js.
In order to achieve that, I made a little application with the following code :
$(document).ready(function() {
var personalToken = btoa(':'+'<personnalAccessToken>');
fetch('https://dev.azure.com/<company>/<project>/_apis/git/repositories?api-version=5.1', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
'Authorization': 'Basic '+ personalToken
}
}).then(function(response) {
return response.json();
}).then(function(repositories) {
console.log("There are "+repositories.count+" repositories");
}).catch(function(error) {
console.log('Fetch error: ' + error.message);
});
This code is working great but as you can see there is my personnalAccessToken writen directly inside the code... which is really bad...
When I am using git in command line, I don't have to specify any credential information because I use git credential manager for windows. Which means my personnalAccessToken is already stored, cached and automatically used everytime I use a git command, like clone, etc.
So, I would like my js code to use the same thing, I would like it to use my stored credentials automatically to fetch the api without being required to set my personnalAccessToken in code.
I have already searched for hours but can't find out if it is possible.
I have already searched for hours but can't find out if it is
possible.
Sorry but as I know it's impossible. The way you're calling the Rest API is similar to use Invoke-RestMethod to call rest api in Powershell.
In both these two scenarios, the process will try to fetch PAT for authentication in current session/context and it won't even try to search the cache in Git Credential Manager.
You should distinguish the difference between accessing Azure Devops service via Rest API and by Code:
Rest API:
POST https://dev.azure.com/{organization}/{project}/{team}/_apis/wit/wiql?api-version=5.1
Request Body:
{
"query": "Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = 'Task' AND [State] <> 'Closed' AND [State] <> 'Removed' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc"
}
Corresponding Code in C#:
VssConnection connection = new VssConnection(new Uri(azureDevOpsOrganizationUrl), new VssClientCredentials());
//create http client and query for resutls
WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
Wiql query = new Wiql() { Query = "SELECT [Id], [Title], [State] FROM workitems WHERE [Work Item Type] = 'Bug' AND [Assigned To] = #Me" };
WorkItemQueryResult queryResults = witClient.QueryByWiqlAsync(query).Result;
Maybe you can consider using a limited PAT, limit its scope to Code only:
I know there exists other Authentication mechanism
:
For Interactive JavaScript project: ADALJS and Microsoft-supported Client Libraries.
You can give it a try but I'm not sure if it works for you since you're not using real Code way to access the Azure Devops Service... Hope it makes some help :)
If you have the script set up in an Azure Runbook you can set it as an encrypted variable there and have it pull it from there before running rather than having it directly written into the code.
$encryptedPatVarName = "ADO_PAT"
$adoPat = Get-AutomationVariable -Name $encryptedPatVarName
$adoPatToken = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($adoPat)"))
$adoHeader = #{authorization = "Basic $adoPatToken"}
The above is the Powershell version of it. I have seen some people do it with other

Avoid new axios request each time vue component is rendered

I'm very much new to Vue so please excuse my lack of knowledge here.
In one of my (child) components (Product_Collection.vue) I make an axios request to get some products from my Shopify store via their GraphQL API:
data: () => {
return {
products: null
}
},
methods: {
getProducts: function(){
// 1. axios code here...
// 2. ...then assign result to "this.products" data property
}
}
The products are displayed as thumbnails (let's say there's 10 t-shirts). I then click a product to view it in more detail with more product info and images etc (very much an Amazon-like experience).
The product is shown on it's own in a Product_Single.vue component. So far so good.
But here's the problem...
When I click back to the products page (Product_Collection.vue) (the page with the 10 t-shirts on) the axios request to the Shopify API gets called again and the component is re-rendered from scratch.
My question is how do I tell Vue to stop fetching the data from the Shopify API each time the component is rendered? Thank you
Sounds like you want cache-control, which Axios supports with an extension.
https://github.com/kuitos/axios-extensions
import axios from 'axios';
import { cacheAdapterEnhancer } from 'axios-extensions';
const http = axios.create({
baseURL: '/',
headers: { 'Cache-Control': 'no-cache' },
// cache will be enabled by default
adapter: cacheAdapterEnhancer(axios.defaults.adapter)
});
http.get('/users'); // make real http request
http.get('/users'); // use the response from the cache of previous request, without real http request made
http.get('/users', { cache: false }); // disable cache manually and the the real http request invoked
Or you could go a more complex route and cache the results yourself into an object, and check if that object exists, but then you'll have to deal with when you want to expire the data in that object.
If you use a cache-control, and cache-control headers and expiry, you can drive all that from the API layer and not have to deal with manually managed stale data in the application.
If you are not using Vue Router, and hiding the list page, you can try a simple option.
Instead of v-if, use v-show. The component will be hidden and displayed again. It won't be re-created.
Also, where do you call the API? From created or mounted hook?
If You are using GraphQL then there is the Apollo state management way which integrates nicely with graphQL. Check this out: https://dev.to/n_tepluhina/apollo-state-management-in-vue-application-8k0
So instead of rewriting the app with axios and adding vuex in the mixture, maybe Apollo client would be more convenient

Facebook Javascript SDK: Can't load a page's public feed [duplicate]

I'm trying to use the Facebook Graph API to get the latest status from a public page, let's say http://www.facebook.com/microsoft
According to http://developers.facebook.com/tools/explorer/?method=GET&path=microsoft%2Fstatuses - I need an access token. As the Microsoft page is 'public', is this definitely the case? Is there no way for me to access these public status' without an access token?
If this is the case, how is the correct method of creating an access token for my website? I have an App ID, however all of the examples at http://developers.facebook.com/docs/authentication/ describe handling user login. I simply want to get the latest status update on the Microsoft page and display it on my site.
This is by design. Once it was possible to fetch the latest status from a public page without access token. That was changed in order to block unidentified anonymous access to the API. You can get an access token for the application (if you don't have a Facebook application set for your website - you should create it) with the following call using graph API:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&
grant_type=client_credentials
This is called App Access Token. Then you proceed with the actual API call using the app access token from above.
hope this helps
You can use AppID and Secret key to get the public posts/feed of any page. This way you don't need to get the access-token. Call it like below.
https://graph.facebook.com/PAGE-ID/feed?access_token=APP-ID|APP-SECRET
And to get posts.
https://graph.facebook.com/PAGE-ID/posts?access_token=APP-ID|APP-SECRET
It's no more possible to use Facebook Graph API without access token for reading public page statuses, what is called Page Public Content Access in Facebook API permissions. Access token even is not enough. You have to use appsecret_proof along with the access token in order to validate that you are the legitimate user. https://developers.facebook.com/blog/post/v2/2018/12/10/verification-for-individual-developers/.
If you are individual developer, you have access to three pages of the data (limited), unless you own a business app.
You can get the posts by simply requesting the site that your browser would request and then extracting the posts from the HTML.
In NodeJS you can do it like this:
// npm i request cheerio request-promise-native
const rp = require('request-promise-native'); // requires installation of `request`
const cheerio = require('cheerio');
function GetFbPosts(pageUrl) {
const requestOptions = {
url: pageUrl,
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:64.0) Gecko/20100101 Firefox/64.0'
}
};
return rp.get(requestOptions).then( postsHtml => {
const $ = cheerio.load(postsHtml);
const timeLinePostEls = $('.userContent').map((i,el)=>$(el)).get();
const posts = timeLinePostEls.map(post=>{
return {
message: post.html(),
created_at: post.parents('.userContentWrapper').find('.timestampContent').html()
}
});
return posts;
});
}
GetFbPosts('https://www.facebook.com/pg/officialstackoverflow/posts/').then(posts=>{
// Log all posts
for (const post of posts) {
console.log(post.created_at, post.message);
}
});
For more information and an example of how to retrieve more than 20 posts see: https://stackoverflow.com/a/54267937/2879085
I had a similar use case for some weeks and I used this API:
https://rapidapi.com/axesso/api/axesso-facebook-data-service/
I could fetch all posts and comments in some minutes, worked quite well for me.

How to request, store, and use an access token in Meteor while using the Instagram API

How does one request, store, and use an access token from an API in the Meteor framework? I am currently trying to make requests from the (Instagram API)[https://instagram.com/developer/authentication/], but I first need to request an access token and store it for later use.
What is the general structure for doing this? I have my Client Id and Client Secret stored in the settings.json and have the services configuration package loaded. I think I need to create some sort of Method using http.get, but if someone could give a brief walkthrough that would be greatly appreciated ! Not much on this in the Meteor Docs.
You can use Bozhao Package for this.
Just install it.
meteor add bozhao:accounts-instagram
And this will work exactly like tha core accounts - facebook || google || twitter
and you can do something like this on the accountsOnCreateUser Methods
if (user.services.instagram) {
console.log("-- REGISTED USER WITH INSTAGRAM ");
instagramProfile = {
socialProfileUrl: user.services.instagram.profile_picture,
socialName: user.services.instagram.full_name,
service: "Instagram",
profileUrl: "https://instagram.com/"+ user.services.instagram.username
};
user.profile = instagramProfile;
}
Now knowing this, you can see that we have the user data inside the user.services.instagram object, there should be a accessToken and id field that you make POST / GET http request to the https://instagram.com/api/v1/.
I have never done a HTTP request to the Instagram API but it should be similar to facebook (if not sorry the below code dosnt help you to much).
Simple http call using the params.
Meteor.http.get("https://instagram.com/api/v1/", {
headers: {
"User-Agent": "Meteor/1.0"
},
params: {
access_token: user.services.accessToken
}
},function(error,result){
if(!error){
console.log(result);
}
});

Categories

Resources