Shopify get Customer Metafields - shopify

UPDATE: I have this Private API request to get a customer metafield data that sends a request to the shopify admin.
const {getAccessToken} = require('./auth')
const request = require('request')
const {shopFromPermanentDomain} = require('./helpers')
const getCustomerMetafields = ({accessToken, id, shop}) => new Promise((resolve, reject) => {
request({
url:`https://${shop}.myshopify.com/admin/customers/${id}/metafields.json',
headers: {
'X-Shopify-Access-Token': accessToken
}
}, (error, response, body) => {
const {errors} = JSON.parse(body)
if (response.statusCode === 200) {
const { metafields } = JSON.parse(body)
resolve(metafields)
}
reject({error: errors, status: 500})
})
})
const getCustomerMetafieldsResponse = (req, res) => {
const {id, permanentDomain} = req.body
if (id && permanentDomain) {
const shop = shopFromPermanentDomain(permanentDomain)
getAccessToken({shop})
.then(accessToken => getCustomerMetafields({accessToken, id, shop})
.then(meta => res.json({
meta,
status: 200
}))
)
.catch(({error, status}) => res.json({error, status}))
} else {
res.json({error: 'Missing params', status: 500})
}
}
module.exports = getCustomerMetafieldsResponse
I make this request from the front-end to my API with the following request.
const getCustomerMeta = ({
id,
permanentDomain
}) => new Promise((resolve, reject) => {
post({
params: { email, permanentDomain, task: 'get-customer-meta' },
then: ({ error, id, state, status }) => {
if (status === 200) {
resolve({ id, state })
}
reject(error)
},
url: '/apps/spoke'
})
})
getCustomerMeta({
id, // 98303739294 (Customer ID)
permanentDomain // "store.myshopify.com"
})
When making this request, I get the following request error:
500 (Internal Server Error)
VM706781:8 Uncaught SyntaxError: Unexpected token < in JSON at position 7
at JSON.parse (<anonymous>)
at XMLHttpRequest.l.onload
I then want to GET the customer metafields data so that I can populate the front-end with the data gathered.
Thanks!

You cannot call the back-end, ie) /admin, from the front-end, without using an App Proxy. On the front-end, the code you'd write would make a Proxy XHR call to your App, with the Customer ID. Using that ID, you'd then get the Metafields for the Customer resource, not the Shop as per your example. With the Metafields resource for the customer, you could then look for the ones that interest you, and draw them on the front-end.
Or just render the metafields for the logged in customer, using Liquid, as that is much simpler.

The api has probably changed drastically since you asked this question in 2018, but now you can get the metafields for the customer via the customers or metafields endpoints.
e.g.
/admin/api/{api-version-number}/customers/{shopify-customer-id}/metafields.json
directy to the metafields endpoint would be:
/admin/api/{api-version-number}/metafields.json?metafield[owner_id]={shopify-customer-id}&metafield[owner_resource]=customers

Related

How to get total member count of any Discord server?

I'm trying to build a scraping script to get a bunch of Discord server's total members. I actually did that with Puppeteer like below but I think my IP address has been banned because I'm getting "Invite Invalid" error from Discord even though invite links are working.
My question is that does Discord have APIs to get any server's total member count? Or is there any 3rd party library for that purpose? Or any other method?
const puppeteer = require('puppeteer')
const discordMembers = async ({ server, browser }) => {
if (!server) return
let totalMembers
const page = await browser.newPage()
try {
await page.goto(`https://discord.com/invite/${server}`, {
timeout: 3000
})
const selector = '.pill-qMtBTq'
await page.waitForSelector(selector, {
timeout: 3000
})
const totalMembersContent = await page.evaluate(selector => {
return document.querySelectorAll(selector)[1].textContent
}, selector)
if (totalMembersContent) {
totalMembers = totalMembersContent
.replace(/ Members/, '')
.replace(/,/g, '')
totalMembers = parseInt(totalMembers)
}
} catch (err) {
console.log(err.message)
}
await page.close()
if (totalMembers) return totalMembers
}
const asyncForEach = async (array, callback) => {
for (let i = 0; i < array.length; i++) {
await callback(array[i], i, array)
}
}
const run = async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
})
const servers = ['tQp4pSE', '3P5K3dzgdB']
await asyncForEach(servers, async server => {
const members = await discordMembers({ server, browser })
console.log({ server, members })
// result
// { server: 'tQp4pSE', members: 57600 }
// { server: '3P5K3dzgdB', members: 159106 }
})
await browser.close()
}
run()
Update: Mar 22, 2022
Thanks for #Vaviloff's answer we can actually access Discord's private APIs but the problem is it's only accessible over browser. I'm getting Request failed with status code 400 issue from Axios. Is it a CORS issue? How do we get the results in a Node.js app?
const axios = require('axios')
const discordMembers = async ({ server }) => {
try {
const apiResult = await axios({
data: {},
method: 'get',
url: `https://discord.com/api/v9/invites/${server}?with_counts=true&with_expiration=true`
})
console.log(apiResult)
} catch (err) {
console.log(err)
}
}
discordMembers({ server: 'tQp4pSE' })
A lot of modern web applications have their own internal APIs. Oftentimes you can spot frontend making requests to it, by using Networking tab in Devtools (filter by Fetch/XHR type):
Such API endpoints can change any time of course, but usually the last for a long time and is a rather convenient way of scraping
Currently Discord uses this URL for basic instance description:
https://discord.com/api/v9/invites/tQp4pSE?with_counts=true&with_expiration=true
By accessing it you get the desired data:
Update
To make your code work don't send any data in the request:
const apiResult = await axios({
method: 'get',
url: `https://discord.com/api/v9/invites/${server}?with_counts=true&with_expiration=true`
})

Call Nextjs API from within Netlify function

I got a serverless Netlify function like this:
exports.handler = async function(event, context) {
return {
statusCode: 200,
body: JSON.stringify({message: "Hello World"})
};
}
When called by this url <site-name>/.netlify/functions/helloworld
I do get the message {"message":"Hello World"}
I also got a pages/api/mailingList.js Nextjs API endpoint:
const axios = require('axios');
export default async function handler(req, res) {
//console.log(req.query.mail);
if (req.method === "PUT") {
axios
.put(
"https://api.sendgrid.com/v3/marketing/contacts",
{
contacts: [{ email: `${req.query.mail}` }],
list_ids: [process.env.SENDGRID_MAILING_LIST_ID],
},
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
},
}
)
.then((result) => {
res.status(200).send({
message:
"Your email has been successfully added to the mailing list. Welcome đź‘‹",
});
})
.catch((err) => {
res.status(500).send({
message:
"Oups, there was a problem with your subscription, please try again or contact us",
});
console.error(err);
});
}
}
This mailing list API endpoint, do work when using curl from the terminal with PUT as the method:
curl -X PUT -d mail=helloworld#gmail.com https://netlify.app/api/mailingList
The API endpoint also work from the URL (/api/mailingList?mail=helloworld#gmail.com) when removing the if (req.method === "PUT") { part from the mailingList.js
However, I am NOT able to get the API endpoint to be called from within the Netlify function.
(Preferably the mailingList API should be possible to call multiple times with different mailing list IDs from the Netlify function helloworld.js based on different logic /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
To get the API endpoint to be called at all, from the function, I have tried adding a axios call from the helloworld.js to mailingList.js like this
const axios = require('axios');
exports.handler = async function(event, context) {
const mail = "helloworld#gmail.com";
// add to mailinglist
axios
.put("/api/mailingList?mail="+mail)
.then((result) => {
if (result.status === 200) {
toast.success(result.data.message);
}
})
.catch((err) => {
console.log(err);
});
}
This result in the following error from the browser: error decoding lambda response: invalid status code returned from lambda: 0
(I do not get any error msg from the Netlify log, either helloworld.js or mailingList.js)
Clearly, there is something wrong with how I call the mailigList.js from helloworld.js. Would greatly appreciate if some one could give me some advice and show me what I am doing wrong.
How can I call the API endpoint (mailigList.js) from within the Netlify function helloworld.js? (Preferably multiple times with different mailing list IDs: /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
Found the solution in this article: https://travishorn.com/netlify-lambda-functions-from-scratch-1186f61c659e
const axios = require('axios');
const mail = "helloworld#gmail.com";
exports.handler = (event, context, callback) => {
axios.put("https://<domain>.netlify.app/api/mailingList?mail="+mail)
.then((res) => {
callback(null, {
statusCode: 200,
body: res.data.title,
});
})
.catch((err) => {
callback(err);
});
};

Can we get product by SKU using Shopify Storefront GraphQL API(NOT ADMIN)?

I am stuck on this for more than two days now. I couldn't able to find any resources on this. There are many solutions by using ADMIN API but I don't have Admin access. Problem I am trying to solve is: I have only access to Product's SKU. I need to fetch all other information(title, price, description, featuredImage etc...) from Shopify using Storefront API. Here's the function to get product:
function loadProducts(items) {
let products = [];
items.forEach((item) => {
const sku = item.id;
if (sku !== "undefined") {
/* TODO: Need to figure out this query*/
const query = `{
products(first: 1, query: "sku:<sku>") {
edges {
node {
title
id
description
}
}
}
}`;
const STOREFRONT_ACCESS_TOKEN = 'xxxxxxxxxxxxxxx';
const GRAPHQL_URL = 'https://<my-store>.myshopify.com/api/2021-01/graphql.json';
const GRAPHQL_BODY = {
'method': 'POST',
'headers': {
'X-Shopify-Storefront-Access-Token': STOREFRONT_ACCESS_TOKEN,
'Content-Type': 'application/json',
},
'body': JSON.stringify({ query })
}
products.push(getData(GRAPHQL_URL, GRAPHQL_BODY));
}
});
return Promise.all(products);
}
function getData(url, body) {
return new Promise((resolve, reject) => {
fetch(url, body)
.then(res => res.json())
.then(data => {
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
I'd really appreciate if you can redirect me to the right direction. PLEASE NOT: I am only suppose to use Storefront API, not the ADMIN API. Thank you!
You can't query the products by SKU using the StoreFront API.
The available query params for the StoreFront products are:
available_for_sale
created_at
product_type
tag
title
updated_at
variants.price
vendor
So you can't do this only with the StoreFront API since the SKU is not exposed (like the Admin API).
There is a workaround if you tag all of your products with their SKUs and you can search for that instead.

How to fetch data from an API using express.js

API
(https://api.mosmarts.com/truck/v0/api.php)
The API is scripted in PHP and accepts GET & POST commands and in return it responds back with a JSON response data.
To retrieve data the API requires “functionality” and “action” among other params as show below.
Command for retrieving all truck
Command for retrieving all truck
Payloads
{
"functionality" : "get",
"action" : "get_all_truck"
}
Command to retrieving truck inspection details by id
Payloads
{
"functionality" : "get",
"action" : "get_inspection_history",
"truckId" : "1"
}
NB: you will get truckId from command "get_all_truck" above
What’s expected from you
As the software developer you are tasked to design and develop a web-based backend solution that will have:
Dashboard: -
• Retrieve and indicate total number of trucks
• Retrieve and indicate number of inspection repairs requested 2. List all Trucks: -
• Implement search option
Inspection List: -
• Implement filter by truck
i have some code using express.js bt i get is a 404 error, no data retrieved.
app.js
const apiCallFromRequest = require('./Request')
const apiCallFromNode = require('./NodeJsCall')
const http = require('http')
http.createServer((req, res) => {
if(req.url === "/request"){
apiCallFromRequest.callApi(function(response){
//console.log(JSON.stringify(response));
res.write(JSON.stringify(response));
res.end();
});
}
else if(req.url === "/node"){
apiCallFromNode.callApi(function(response){
res.write(response);
res.end();
});
}
// res.end();
}).listen(3000);
console.log("service running on 3000 port....");
NodeJsCall.js
const https = require('https');
_EXTERNAL_URL = 'https://api.mosmarts.com/truck/v0/api.php';
const callExternalApiUsingHttp = (callback) => {
https.get(_EXTERNAL_URL, (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
return callback(data);
// console.log(JSON.stringify(data));
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
module.exports.callApi = callExternalApiUsingHttp;
Request.js
const request = require('request');
_EXTERNAL_URL = 'https://api.mosmarts.com/truck/v0/api.php';
const callExternalApiUsingRequest = (callback) => {
request(_EXTERNAL_URL, { json: true }, (err, res, body) => {
if (err) {
return callback(err);
}
return callback(body);
});
}
module.exports.callApi = callExternalApiUsingRequest;
Hey Gerald you can find a simple response for this kind of question on google.
if you are a real beginner I would propose you the Axios npm.
here is an example of a really simple GET request with axios.
axios.get('https://api.github.com/users/mapbox')
.then(response => {
console.log(response.data.created_at);
});

Mailchimp - Delete all products

I'm trying to clean up old products from a mailchimp database using API v3.0.
Mailchimp's E-commerce documentation explains how to delete a single product using:
DELETE /ecommerce/stores/{store_id}/products/{product_id}
Is there a way to delete all products at once? I cannot do it manually since I have thousands of records to remove.
I couldn't find a way to delete all products at once, so I wrote a NodeJS script to delete products in batches :
// delete-script.js
const request = require('request')
const headers = { 'Authorization': 'Basic XXX-usX' }
const productsUri = 'https://usX.api.mailchimp.com/3.0/ecommerce/stores/XX/products'
const erasePage = request.bind(this, {
uri: `${productsUri}?count=1000`,
method: "GET",
headers
}, (err, res, body) => {
const products = JSON.parse(body).products
const length = products.length
if (length === 0) return
let resolvedDeletions = 0
products.forEach(({ id }) => {
request({
uri: `${productsUri}/${id}`,
method: 'DELETE',
headers
}, err => {
if (err) return console.error(err)
console.log('deleted ', id)
if (++resolvedDeletions === length) erasePage()
})
})
})
erasePage()
Simply npm install request then execute script with node delete-script.js