shopify / shopify-api-js Goto Github PK
View Code? Open in Web Editor NEWShopify Admin API Library for Node. Accelerate development with support for authentication, graphql proxy, webhooks
License: MIT License
Shopify Admin API Library for Node. Accelerate development with support for authentication, graphql proxy, webhooks
License: MIT License
This fails when session is present with scope undefined.
``
if (session) {
const scopesChanged = !Shopify.Context.SCOPES.equals(session.scope);
if (!scopesChanged && session.accessToken && (!session.expires || session.expires >= new Date())) {
ctx.cookies.set(TOP_LEVEL_OAUTH_COOKIE_NAME);
await next();
return;
}
}
``
Should invalidate session and initiate oauth.
What actually happens?
The failure happens downstream in shopify api,
TypeError: Cannot read property 'map' of undefined at new AuthScopes (/node_modules/@shopify/shopify-api/dist/auth/scopes/index.js:14:35)
I'm unable to reproduce this reliably, but can manually remove scope from session to test.
Tool used to check that all links in Markdown files are valid gets rate-limited when checking changelog PR links. Causes CI to fail because some links return 429 Too Many Requests
.
https://github.com/Shopify/shopify-node-api
written in ts
https://github.com/Shopify/shopify-demo-app-node-react
in JS
they recomend not use Memory storage for sessions for production
but not suppy docs or examples how to implement it in pure JS
but only TS example with redis
after i try to implement my own storage callbacks in pure JS i get
SessionStorageError: Expected return to be instanceof Session, but received instanceof Object
I'm building a custom shopify app, where I register a webhook for SUBSCRIPTION_CONTRACTS_CREATE
and SUBSCRIPTION_CONTRACTS_UPDATE
topic.
I expect the webhook to be registered successfully, like when I register PRODUCTS_CREATE
topic.
The webhook isn't registered, and I get this traceback:
(node:53083) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'webhookSubscriptions' of null
at Object.<anonymous> (/Users/sophie/Projects/shopify-app/node_modules/@shopify/shopify-api/dist/webhooks/registry.js:92:44)
at step (/Users/sophie/Projects/shopify-app/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:143:27)
at Object.next (/Users/sophie/Projects/shopify-app/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:124:57)
at fulfilled (/Users/sophie/Projects/shopify-app/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:114:62)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
afterAuth
: Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: '/webhooks',
topic: 'SUBSCRIPTION_CONTRACTS_CREATE',
apiVersion: ApiVersion.April21, // I also tried '2021-07' and ApiVersion.January21
webhookHandler: (_topic, shop, _body) => { console.log('Subscription Contract was created!'); }
});
On the server side, when calling await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res)
, the function is supposed to call the loadCallback
for the Shopify.Session.CustomSessionStorage
. However, it does not always call this function when invoked, and immediately returns undefined
, which causes the app to re-auth and get stuck in an infinite Oauth loop.
await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res)
should always call the loadCallback
for the Shopify.Session.CustomSessionStorage
. Its value should be undefined
only if loadCallback
returns undefined, but should otherwise contain a Session
object.
During auth and app loading, Shopify.Utils.loadCurrentSession
calls the loadCallback
function. However, after auth when handleRequest
is called, and the Shopify.Utils.loadCurrentSession
is called again, it immediately (less than 1ms) returns undefined
and loadCallback
is NOT run.
Shopify.Context.initialize({
API_KEY: API_KEY,
API_SECRET_KEY: SHOPIFY_API_SECRET,
SCOPES: SHOPIFY_API_SCOPES.split(","),
HOST_NAME: SHOPIFY_APP_URL.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.April21,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
storeCallback,
loadCallback,
deleteCallback,
),
});
async function loadCallback(id) {
//This is NOT called after handleRequest()
//Load session from db
session = await getFromDb();
if(session) {
return session;
} else {
return undefined;
}
});
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
const currentSession = await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res);
if(!currentSession) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
Shopify.Utils.loadCurrentSession: undefined
Redirect to /auth
storeCallback: Store new temp session #1 (random id, with partial data)
Shopify.Utils.loadCurrentSession (loadCallback runs): Load temp session #1
storeCallback: Store shop session (with myshopify url in id)
storeCallback: Store temp session #1 again (with full data)
Shopify.Utils.loadCurrentSession (loadCallback runs): Load temp session #1 again
Redirect to /
Shopify.Utils.loadCurrentSession (loadCallback runs): Load temp session #1 again
Load app (handleRequest())
Shopify.Utils.loadCurrentSession (**loadCallback does NOT run**): Attempt to load current online session (but immediately returns undefined)
Redirect to /auth
Process repeats until app throws error: "The app couldn’t be loaded This app can’t load due to an issue with browser cookies."
Hi shopify-node-api team,
Thanks for making this node API.
I am trying to build an embedded app with Vue/Koa.js, I just start by following the tutorial of @shopify/koa-shopify-auth. However, when I tried to run the example app via command node server.js
,
it return an error said:
Shopify.Context.initialize({
^
TypeError: Cannot read property 'initialize' of undefined
It confused me, so I came here following the getting start tutorial of @shopify/shopify-node-api, and carefully checked every input arguments(API_KEY, API_SECRET_KEY, SCOPES, HOST, all these value are correct).
Unfortunately, I got just the same error.
I expected the example app can successfully executed, or at least the function Shopify.Context.initialize
could work.
when I tried to run the example app via command node server.js
,
it return an error said:
Shopify.Context.initialize({
^
TypeError: Cannot read property 'initialize' of undefined
Even I checked the tutorial at here, it still end with same the error.
node server.js
Following is my code(sever.js and package.json):
server.js
import 'isomorphic-fetch';
import dotenv from 'dotenv';
import Koa from 'koa';
import session from 'koa-session';
import shopifyAuth, {default as verifyRequest} from '@shopify/koa-shopify-auth';
import Shopify, {ApiVersion} from '@shopify/shopify-api';
dotenv.config();
const port = parseInt(process.env.port, 10) || 3000;
const {SHOPIFY_API_SECRET, SHOPIFY_API_KEY} = process.env;
//initialize the library
Shopify.Context.initialize({
API_KEY: SHOPIFY_API_KEY,
API_SECRET_KEY: SHOPIFY_API_SECRET,
SCOPES: [process.env.SHOPIFY_APP_SCOPES],
HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/^https:\/\//, ''),
IS_EMBEDDED_APP: true,
API_VERSION: ApiVersion.October20,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage()
});
const server = new Koa();
server.keys = [SHOPIFY_API_SECRET];
server.use(session(server))
.use(
shopifyAuth({
afterAuth(ctx){
const {shop, accessToken} = ctx.session;
console.log('First Trial Success!', accessToken);
ctx.redirect('/');
},
}),
)
.use(verifyRequest())
.use(ctx => {
ctx.body = '😎';
});
server.listen(port, ()=>{
console.log('> Ready on http://localhost:${port}');
});
package.json
{
"name": "koala",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Dan Hsu",
"license": "ISC",
"dependencies": {
"@shopify/app-bridge-utils": "^1.29.0",
"@shopify/koa-shopify-auth": "^4.0.2",
"@shopify/shopify-api": "^1.1.0",
"@types/koa": "^2.13.1",
"dotenv": "^8.2.0",
"isomorphic-fetch": "^3.0.0",
"koa": "^2.13.1",
"koa-router": "^8.0.8",
"koa-session": "^6.1.0"
}
}
Just like it says in the title! ☝🏻
When a user is on the "You are about to install #APP_NAME" page and they wait for more than 1 minute to click "Install app" a 500 error is thrown.
Url
GET /auth/callback?code=4e9c4cb180763a21ab0fbf243c17f5ba&hmac=751bbcaa7f5a4013575d617af11f23eb1e570c8c1f82d36a12405895404bda38&host=cXVpdmVyLWltbWVkaWF0ZS1kZWxpdmVyaWVzLm15c2hvcGlmeS5jb20vYWRtaW4&shop=quiver-immediate-deliveries.myshopify.com&state=284790218494132×tamp=1623885746
Error
InternalServerError: Cannot complete OAuth process. Could not find an OAuth cookie for shop url: quiver-immediate-deliveries.myshopify.com
at Object.throw (/app/node_modules/koa/lib/context.js:97:11)
at /app/node_modules/@shopify/koa-shopify-auth/dist/src/auth/index.js:100:42
at step (/app/node_modules/tslib/tslib.js:141:27)
at Object.throw (/app/node_modules/tslib/tslib.js:122:57)
at rejected (/app/node_modules/tslib/tslib.js:113:69)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
This bug is difficult to recreate as installing the app on a test store, you typically don't go through the App store. This means the "You are about to install #APP_NAME" is skipped as it "Install app" is auto-clicked/auto-selected.
To understand this error better, I added CustomSessionStorage
class with the relevant functions. The loadCallback function is never called if the time between auth is longer than 1 minute even though there is a session in the storage.
This issue occurs when using the Shopify.Session.MemorySessionStorage
and Shopify.Session.CustomSessionStorage
.
My relevant dependencies are:
"@shopify/app-bridge": "^2.0.3",
"@shopify/app-bridge-react": "^2.0.3",
"@shopify/app-bridge-utils": "^2.0.3",
"@shopify/koa-shopify-auth": "^4.1.3",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-ignore": "^1.0.1",
"koa-logger": "^3.2.1",
"koa-router": "^8.0.8",
"shopify-api-node": "^3.6.12",
Users should be able to sit for more 1 minute idle between auth and auth callback
OR
I should be able to handle this error and restart the auth process rather than just throwing 500 error.
I have been recreating the issue via shopify.
Using import Shopify, {ApiVersion} from '@shopify/shopify-api';
When I print out the available ApiVersions, I get an old list:
ApiVersions: {
July19: '2019-07',
October19: '2019-10',
January20: '2020-01',
April20: '2020-04',
July20: '2020-07',
October20: '2020-10',
Unstable: 'unstable',
Unversioned: 'unversioned'
}
Seems that these are cached values. How do I get the latest API versions to load?
I had created a sample app using the shopify cli tool (shopify create
) and I wanted to add additional routes to the existing app. I have created a route called /api/orders
which returns a list of all the orders.
server.js
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.get("/api/orders", verifyRequest(), async (ctx) => {
console.log(ACTIVE_SHOPIFY_SHOPS);
const session = await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res);
const client = new Shopify.Clients.Graphql(
session.shop,
session.accessToken
);
// console.log("Loaded token: ", session.accessToken)
// console.log("cookies: ", ctx.cookies.get('accessToken'))
ctx.body = await api_orders(client, ctx.params, ctx.query); // returns JSON string });
After starting the server (shopify serve
), I go ahead and authenticate myself by opening the auth URL (https://<ngrock_url>/auth?shop=shopname.myshopify.com
) in my browser.
Once I'm done with that I can get the orders by opening the defined route in my browser (https://<ngrock_url>/api/orders
). However when I open the url again after a few seconds, it redirects me to a different unrecognized url (https://admin/oauth/authorize?client_id=e74e84f43b273631f1asdfccbd1d6607f2bc&scope=read_orders&redirect_uri=https%3A%2F%2F3960a5286b424.ngrok.io%2Fauth%2Fcallback&state=233468616505697&grant_options%5B%5D=per-user
)
https://<ngrock_url>/api/orders
url continues to return a list of orders even after some time has passed.
Opening the url after some time redirects to an unknown page or returns TypeError: Cannot read property 'map' of undefined
.
TypeError: Cannot read property 'map' of undefined
at new AuthScopes (/home/gts/Projects/personal/shopify/sample_app/node_modules/@shopify/shopify-api/dist/auth/scopes/index.js:14:35)
at AuthScopes.equals (/home/gts/Projects/personal/shopify/sample_app/node_modules/@shopify/shopify-api/dist/auth/scopes/index.js:38:21)
at /home/gts/Projects/personal/shopify/sample_app/node_modules/@shopify/koa-shopify-auth/dist/src/verify-request/verify-token.js:23:79
at step (/home/gts/Projects/personal/shopify/sample_app/node_modules/tslib/tslib.js:133:27)
at Object.next (/home/gts/Projects/personal/shopify/sample_app/node_modules/tslib/tslib.js:114:57)
at fulfilled (/home/gts/Projects/personal/shopify/sample_app/node_modules/tslib/tslib.js:104:62)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
shopify create node
)/api/orders
route in server.js
and add read_orders
scope to .env
TypeError: Cannot read property 'map' of undefined
in auth/scopes
This is not an issue, but it's kind of confusing as both libraries can make Shopify Admin API requests. Which one is preferred?
Getting error with message: Cannot complete OAuth process. Could not find an OAuth cookie for shop url:
validateAuthCallback
shouldn't throw an error
/auth/callback
route throws an error
This happens while validating auth callback (route: /auth/callback)
I used the code from the docs (README.md)
Shopify.Context.initialize({
API_KEY: settings.SHOPIFY_API_KEY,
API_SECRET_KEY: settings.SHOPIFY_API_SECRET,
SCOPES: settings.SHOPIFY_API_SCOPES.split(','),
HOST_NAME: settings.HOST.replace(/https:\/\//, ''),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage()
})
/login
router
.get('/login', async (req: Request, res: Response) => {
const { shop } = req.query
if (typeof shop !== 'string' || !Shopify.Utils.validateShop(shop)) {
res.status(400).send('Invalid shop parameter')
return
}
try {
const authRoute = await Shopify.Auth.beginAuth(
req,
res,
shop,
'/auth/callback',
true
)
return res.redirect(authRoute)
} catch (error) {
log.error(error)
res.status(500).send('Unexpected error occured')
}
})
/auth/callback
router
.get('/auth/callback', async (req: Request, res: Response) => {
try {
await Shopify.Auth.validateAuthCallback(
req,
res,
(req.query as unknown) as AuthQuery
)
return res.redirect('/')
} catch (error) {
log.error(error)
res.status(500).send('Unexpected error occured')
}
})
/auth?shop=your.shop.url
I have a fresh embedded app created via the CLI and when I use embedded navigation it doesn't load a page, it instead redirects to an enable cookies url, then redirects to auth and loads the initial default view again once complete.
What do you think should happen?
I would expect that when clicking a navigation item it would load a page from the Next.js app.
What actually happens?
I created a brand new Shopify app using the CLI. I duplicated pages/index.js
to pages/sample.js
. I then went to App Setup > Embedded app > Configure (Navigation) inbox the partner dashboard. I added two items, Dashboard > / and Sample > /sample.
The navigation appears as expected, when I click on the "Sample" link I would expect it to load the sample page from Next.js. However it seems to redirect to an enable cookies url, then redirects to auth and loads the initial default view again once complete.
Can we add option for adjusts the request rate so that the rate limit is not reached?
Currently, all developers who use the Shopify api need to take care of the rate limit on their own.
I thought it would be easier for many developers to use this library if it was implemented as an option in this library.
The package gives us CustomSessionStorage
and requires to implement 3 callbacks which are storeCallback
, loadCallback
, deleteCallback
. The storeCallback
method requires Session
argument when using Typescript. How do I store my own session data? For example, let's say I want to store the online and offline access token at the same time, but it looks like the current implementation only allows one token. And even if I can extend Session
with my own class, I'm forced to store shop
, state
and scope
. I'm not sure what the state
property is for. Is there any way I can further customize it?
class Session {
readonly id: string;
static cloneSession(session: Session, newId: string): Session;
shop: string;
state: string;
scope: string;
expires?: Date;
isOnline?: boolean;
accessToken?: string;
onlineAccessInfo?: OnlineAccessInfo;
constructor(id: string);
}
Hi there,
Thanks for making this node API!
I've been making a test development app to learn more about how cookieless sessions should work in Shopify apps.
However, Shopify.Utils.loadCurrentSession
is suddenly failing to parse session tokens provided by making a request to API routes using authenticatedFetch
from @shopify/app-bridge-utils
. This was working late last week, but when I loaded my development app today, this suddenly started failing with no changes on my part.
InvalidJwtError: Failed to parse session token 'base64 string here': jwt not active at InvalidJwtError.ShopifyError [as constructor] (\node_modules\@shopify\shopify-api\dist\error.js:13:28)
authenticatedFetch
should request the data from my test route and attach the Authorization
header, which is then parsed within the Shopify object on the back end and return a session, which I can check the presence of, and complete the API request with dummy data.
The session now fails to load in the /test
route, due to a failure to parse the provided token that the authenticatedFetch is attaching to the request. The base 64 string matches in the backend error logs and on the front end network request, though:
Frontend request
Authorization | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvcHJvZHVjdC1vcHRpb25zLXRlc3QxNS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvcHJvZHVjdC1vcHRpb25zLXRlc3QxNS5teXNob3BpZnkuY29tIiwiYXVkIjoiODUxOWFmNzMzZTRhNThlNDBlNDlkZjhmMWVkM2U4YTAiLCJzdWIiOiIzNjA0MzQ4OTMyOCIsImV4cCI6MTYxNTgzMTAwMSwibmJmIjoxNjE1ODMwOTQxLCJpYXQiOjE2MTU4MzA5NDEsImp0aSI6IjVkZjRmODgzLTY5YWMtNGY3ZC05ZjhkLWNlYzQ2YWIwYjg3OSIsInNpZCI6ImI3YTM5ODg1MjRhNzI0NDgyZjYyMWNjODg2ZDI1M2ExOGQ5NjcyMmZkMjhmNzY2ZGZhNTEwMzQxM2FhZmVjNDUifQ.O7E8xBPyzskfomLuGwz_0BjnpY6Az6vqk9mDjHXbJTQ
server error
InvalidJwtError: Failed to parse session token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvcHJvZHVjdC1vcHRpb25zLXRlc3QxNS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvcHJvZHVjdC1vcHRpb25zLXRlc3QxNS5teXNob3BpZnkuY29tIiwiYXVkIjoiODUxOWFmNzMzZTRhNThlNDBlNDlkZjhmMWVkM2U4YTAiLCJzdWIiOiIzNjA0MzQ4OTMyOCIsImV4cCI6MTYxNTgzMTAwMSwibmJmIjoxNjE1ODMwOTQxLCJpYXQiOjE2MTU4MzA5NDEsImp0aSI6IjVkZjRmODgzLTY5YWMtNGY3ZC05ZjhkLWNlYzQ2YWIwYjg3OSIsInNpZCI6ImI3YTM5ODg1MjRhNzI0NDgyZjYyMWNjODg2ZDI1M2ExOGQ5NjcyMmZkMjhmNzY2ZGZhNTEwMzQxM2FhZmVjNDUifQ.O7E8xBPyzskfomLuGwz_0BjnpY6Az6vqk9mDjHXbJTQ': jwt not active at InvalidJwtError.ShopifyError [as constructor]
InvalidJwtError: Failed to parse session token 'base64 string here: jwt not active at InvalidJwtError.ShopifyError [as constructor] (\node_modules\@shopify\shopify-api\dist\error.js:13:28)
at new InvalidJwtError (\node_modules\@shopify\shopify-api\dist\error.js:39:42)
at Object.decodeSessionToken (\node_modules\@shopify\shopify-api\dist\utils\decode-session-token.js:20:15)
at Object.getCurrentSessionId (\node_modules\@shopify\shopify-api\dist\auth\oauth\oauth.js:218:64)
at Object.<anonymous> (\node_modules\@shopify\shopify-api\dist\utils\load-current-session.js:19:46)
at step (\node_modules\@shopify\shopify-api\node_modules\tslib\tslib.js:143:27)
at Object.next (\node_modules\@shopify\shopify-api\node_modules\tslib\tslib.js:124:57)
at \node_modules\@shopify\shopify-api\node_modules\tslib\tslib.js:117:75
at new Promise (<anonymous>)
at Object.__awaiter (\node_modules\@shopify\shopify-api\node_modules\tslib\tslib.js:113:16)
at Object.loadCurrentSession (\node_modules\@shopify\shopify-api\dist\utils\load-current-session.js:15:20)
getTestData
will make the request to the api route specified in server.jsShopify.Utils.loadCurrentSession
. This was working on Friday with the below reduced test case, but now it's not.server.js
require("isomorphic-fetch");
const dotenv = require("dotenv");
dotenv.config();
const Koa = require("koa");
const next = require("next");
const { default: createShopifyAuth } = require("@shopify/koa-shopify-auth");
const { verifyRequest } = require("@shopify/koa-shopify-auth");
const { default: Shopify, ApiVersion } = require("@shopify/shopify-api");
const Router = require("@koa/router");
const { SHOPIFY_APP_SECRET, SHOPIFY_APP_KEY } = process.env;
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_APP_KEY,
API_SECRET_KEY: process.env.SHOPIFY_APP_SECRET,
SCOPES: ["read_products", "write_products"],
HOST_NAME: process.env.BASE_URL.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.January21,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
accessMode: "offline",
async afterAuth(ctx) {
const { shop, scope, accessToken } = ctx.state.shopify;
console.log({ shopify: ctx.state.shopify });
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
console.log({ ACTIVE_SHOPIFY_SHOPS });
ctx.redirect(`/?shop=${shop}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.get("/test", async (ctx) => {
console.log("before load session");
const session = await Shopify.Utils.loadCurrentSession(
ctx.request,
ctx.response,
false
);
console.log({ session });
return (ctx.body = { it: "worked" });
});
router.get("(/_next/static/.*)", handleRequest);
router.get("/_next/webpack-hmr", handleRequest);
router.get("/__nextjs_original-stack-frame", handleRequest);
router.get("(.*)", verifyRequest(), handleRequest);
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
index.js - Main Page of React App
import { authenticatedFetch } from "@shopify/app-bridge-utils";
async componentDidMount() {
this.getTestData();
}
getTestData = async () => {
const app = this.context;
const response = await authenticatedFetch(app)(
`${APP_URL}/test`
).then(
(result) => {
console.log({ result });
},
(error) => {
this.setState({ error });
}
);
};
How to pass query parameter to the client.get()?
I have tried passing the query key as shown below (and some other variations with .json and ?) but I get the whole assets array. I am not sure if I am doing it incorrectly or it's a bug.
client.get({
path: `themes/${mainTheme.id}/assets`,
query: "asset[key]=templates/index.liquid",
})
However, I am able to get the desired result using axios.
const config = {
baseURL: `https://${session.shop}`,
headers: {
"X-Shopify-Access-Token": session.accessToken,
"X-Shopify-Shop-Domain": session.shop,
},
};
const result = await axios.get(
`/admin/api/2021-04/themes/${mainTheme.id}/assets.json?asset[key]=templates/index.liquid`,
config
);
Please update the docs with more examples.
Write a short description of the issue here ↓
I am setting up an app-uninstalled webhook as in this tutorial, but it seems the webhook handler hangs at
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
The full handler code is here
router.post("/webhooks", async (ctx) => {
// eslint-disable-next-line no-console
console.log(`Webhook request received`);
try {
// NEXT LINE HANGS FOREVER
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
} catch (err) {
console.error(err);
err.status = err.statusCode || err.status || 500;
throw err;
}
// eslint-disable-next-line no-console
console.log(`Webhook processed with status code 200`);
});
And the webhook handler is registered like this
server.use(
createShopifyAuth({
async afterAuth(ctx) {
const { shop, scope, accessToken } = ctx.state.shopify;
await firestore.saveShop(shop, scope, accessToken);
const registration = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (_topic, shop, _body) => {
// eslint-disable-next-line no-console
console.info(`App uninstalled from ${shop}`);
await firestore.deleteShop(shop);
},
});
if (registration.success) {
// eslint-disable-next-line no-console
console.info("Successfully registered APP_UNINSTALLED webhook.");
} else {
// eslint-disable-next-line no-console
console.info(
"Failed to register APP_UNINSTALLED webhook",
registration.result
);
}
ctx.redirect(
`https://${CWA_HOST}/shopify&shop=${shop}&accessToken=${accessToken}`
);
},
})
);
Any ideas Shopify.Webhooks.Registry.process
simply hangs without throwing an error?
What do you think should happen?
What actually happens?
Tip: include an error message (in a <details></details>
tag) if your issue is related to an error
The best way to get your bug fixed is to provide a reduced test case.
I want to use Shopify.Utils.graphqlProxy with NextJS (API-Routes) without koa.
like this:
pages/api/graphql.ts
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
await Shopify.Utils.graphqlProxy(req, res);
};
But these two events do not fire.
https://github.com/Shopify/shopify-node-api/blob/bc70452e05de5f9209cdf21e0bf22edde3cce47f/src/utils/graphql_proxy.ts#L22
https://github.com/Shopify/shopify-node-api/blob/bc70452e05de5f9209cdf21e0bf22edde3cce47f/src/utils/graphql_proxy.ts#L26
I have confirmed that I can proxy with the following code.
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await Shopify.Utils.loadCurrentSession(req, res);
if (!session) {
throw new Error("Cannot proxy query. No session found.");
} else if (!session.accessToken) {
throw new Error("Cannot proxy query. Session not authenticated.");
}
const shopName: string = session.shop;
const token: string = session.accessToken;
const options = {
data: req.body,
};
const client = new Shopify.Clients.Graphql(shopName, token);
const response = await client.query(options);
res.status(200).json(response.body);
};
Could you give me some advice about this?
So far i was sure https://github.com/MONEI/Shopify-api-node is official package. Which one is recommended to use by Shopify and why?
Now that the library has been released, we noticed a couple of pain points that we can help remedy via documentation. This PR aggregates them so we can address them at once.
withSession
docs).CustomSessionStorage
in the session notes. We could show how to implement the callbacks using Redis.I am wondering if there is a way to check if the user (store) is authenticated when the request comes from the Shopify proxy? How do I get a session for a store (offline/online) when a user (store) has sent a request to the app's server using a script that is injected with a script tag? Or are there any other ways to check if the request sent with exactly Shopify app proxy? The current problem is that you can fetch store data from the app using any shop parameter in the request and all requests coming to the path /proxy/*
bypass authentication check
I saw that a PR is currently in progress for updating documentation related to the GraphQL client, so thought I'd flag here.
The GraphqlParams
type defines an optional query property with type Record<string, string | number>
. In other words, query parameters must be an object with values of either string or number.
Some mutations require passing an object as a parameter value. For example eventBridgeWebhookSubscriptionCreate
requires a webhookSubscription
parameter of type EventBridgeWebhookSubscriptionInput
. Attempting to pass a value like that results in type errors.
I'm not completely familiar with all queries/mutations offered by the Admin API GraphQL endpoint and their required parameters, so I'm not sure what this type should be.
I'm attempting to use the GraphQL client provided by this library to create EventBridge subscriptions, but I'm seeing type errors. Apologies if I've misunderstood how the GraphQL client works, I understand documentation is still in progress. Thanks!
Allow mutations with GraphQLClient and not just queries.
...
What inspired this feature request? What problems were you facing?
Wanting to make mutations using the shopify GraphQLClient
...
Area: <area>
labels to this issueThis may not be an issue and may be a gap in my knowledge of how Shopify is implementing Auth0 and how that works with this library, so I apologize in advance if these issues are unrelated.
I am creating a non-embedded Shopify app. This app should allow users to authenticate without downloading the app from the Shopify store, but instead visiting a route. Like the library docs specified, I have a /login
route that uses the Shopify context that was setup to create a authRoute and upon trying to redirect using the response from the route handler I get a cors error from Auth0. Is there a different way to initiate authentication for a non-embedded app without using the shopify store but a custom route from the app?
Auth0 recommends passing this authRoute to the UI and doing a window.location change, this does not include the cookies needed for authentication in the validateAuthCallback function so this workaround wouldn't work here.
What do you think should happen?
I should be able to allow the user to authenticate using the /login
route. The user specifies their shop name -> {shopname}.myshopify.com
and we use this to authenticate them using the documented ways in this library's documentation without receiving cors issues from Auth0.
What actually happens?
Auth0 throws cors errors when the authentication process is initiated from my app (ngrok link)
Tip: include an error message (in a <details></details>
tag) if your issue is related to an error
The best way to get your bug fixed is to provide a reduced test case.
It would be great to have types of each of the REST Admin API Shopify objects (e.g. Orders, Products, etc...) defined in this library (or maybe in a separate dedicated library of types) so that partners can have a type safe way of handling Shopify REST API responses.
This library doesn't have these types and other community based libraries that have defined types are unreliable and oftentimes out of date.
What inspired this feature request? What problems were you facing?
I'd like to have types of Shopify REST objects so that I can have reliable types and make more reliable Shopify apps.
I am working on using a AWS lambda function (which is behind a API gateway route). I created a mocked IncomingMessage and ServerResponse object using node-mocks-http. I am using Serverless framework.
Can we add support for AWS API gateway, AWS Lambda, and Serverless framework?
My code
export const getAuth = async (event: any): Promise<any> => {
try {
Shopify.Context.initialize({
API_KEY: String(process.env.SHOPIFY_API_KEY),
API_SECRET_KEY: String(process.env.SHOPIFY_API_SECRET),
SCOPES: String(process.env.SHOPIFY_API_SCOPES).split(","),
HOST_NAME: String(process.env.SHOPIFY_APP_URL).replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const request:any = httpMocks.createRequest({
method: event.httpMethd,
url: event.resource,
params: event.pathParameters,
});
console.log(request);
const response = httpMocks.createResponse();
try {
const authRoute = await Shopify.Auth.beginAuth(request, response, SHOP, '/auth/callback');
return reply({ 'Location': authRoute }, httpStatuses.MOVED_TEMPORARILY);
}
catch (e) {
console.log(e);
return replyError(e);
}
} catch (e) {
return replyError(e);
}
};
export function handler(event, context):any {
const collectionHandlers = {
POST: null,
GET: getAuth,
};
const itemHandlers = {
PUT: null,
DELETE: null,
};
return router(collectionHandlers, itemHandlers, { event, context });
}
I am getting the following response
TypeError: Cannot set property 'set-cookie' of undefined
at EventEmitter.setHeader (_http_outgoing.js:556:31)
at Cookies.set (/Users/umair/Desktop/code/shopify/category_management/backend/node_modules/cookies/index.js:116:13)
at Object.<anonymous> (/Users/umair/Desktop/code/shopify/category_management/backend/node_modules/@shopify/shopify-api/dist/auth/oauth/oauth.js:55:33)
at step (/Users/umair/Desktop/code/shopify/category_management/backend/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:143:27)
at Object.next (/Users/umair/Desktop/code/shopify/category_management/backend/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:124:57)
at fulfilled (/Users/umair/Desktop/code/shopify/category_management/backend/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:114:62)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
2021-03-31 19:05:10 [error]: TypeError: Cannot set property 'set-cookie' of undefined
Add 2021-04 in the versions enum in base_types.ts
2021-04 entry is added
Ability to make GQL call taretting the 2021-04 version
Hello,
I get this error when trying to include the @shopify/shopify-api package in my project:
./node_modules/@shopify/shopify-api/dist/clients/http_client/http_client.js:7:0
Module not found: Can't resolve 'fs'
Checking that file, it seems that indeed that script is trying to use 'fs', on line 7:
var fs_1 = tslib_1.__importDefault(require("fs"));
That package is not available anymore on the NPM repository: https://www.npmjs.com/package/fs
when accessMode is offline graphql_proxy doesn't work.
you need to pass a parameter to the graphqlProxy to let it know the accessMode.
https://github.com/Shopify/shopify-node-api/blob/766317d6419d0f8b1600e917f63de21f91f65106/src/utils/graphql_proxy.ts#L9
As the accessMode is not being passed to the graphql_proxy, it tries to get the online session as it does not exist and throws a throw below.
Error: Cannot proxy query. No session found.
at SessionNotFound.ShopifyError [as constructor] (/home/ubuntu/src//node_modules/@shopify/shopify-api/dist/error.js:13:28)
at new SessionNotFound (/home/ubuntu/src//node_modules/@shopify/shopify-api/dist/error.js:140:42)
at Object.<anonymous> (/home/ubuntu/src//node_modules/@shopify/shopify-api/dist/utils/graphql_proxy.js:17:31)
at step (/home/ubuntu/src//node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:143:27)
at Object.next (/home/ubuntu/src//node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:124:57)
at fulfilled (/home/ubuntu/src//node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:114:62)
Hello,
I'm using this guide to setup webhooks in my Shopify app.
Specifically I have the following code:
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const registration = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: '/webhooks',
topic: 'APP_UNINSTALLED',
apiVersion: ApiVersion.October20,
webhookHandler: (_topic, shop, _body) => {
console.log('App uninstalled');
delete ACTIVE_SHOPIFY_SHOPS[shop];
},
});
if (registration.success) {
console.log('Successfully registered webhook!');
} else {
console.log('Failed to register webhook', registration.result.data.webhookSubscriptionCreate.userErrors);
}
However the webhook fails to register each time giving me the error:
Failed to register webhook [
{
field: [ 'webhookSubscription', 'callbackUrl' ],
message: 'Address is invalid'
}
]
Any ideas - I can't see anywhere to specify the callback / where is it getting it from to call it invalid?
Thanks.
Maybe more of a question as I'm assuming I may have misinterpreted the docs.
When registering EventBridge webhooks, the arn
argument looks like it's built as a URL in the following format:
https://${Context.HOST_NAME}${path}
This tutorial suggests using the Partner event source ARN which as far as I can tell, for Shopify events, follow the format:
arn:aws:events:region::event-source/aws.partner/shopify.com/id/name
Is there a way to set the ARN argument manually, or am I missing something here? Thanks.
I tried to figure out how to make post requests to the sopify admin rest api but I can't figure it out. That's what I expected the request to look like:
const client = new Shopify.Clients.Rest(shop, accessToken);
await client.post({
path: 'script_tags',
data: {
script_tag: {event: "onload", src: "url"}
}
}).catch(console.log)
This request resolves in an Error:
TypeError [ERR_INVALID_ARG_TYPE]: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received null
There is no documentation provided on what format the data parameter should have.
With this kind of request
client.get({
path: 'redirects',
query: {
"path": 'some_path',
},
});
I get error, because limit is undefined
Successful request to the api
I get error UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'toString' of undefined
which comes from this row https://github.com/Shopify/shopify-node-api/blob/7c9201af556c6f82deb936215f5099d08e35f727/src/clients/rest/rest_client.ts#L35
Add a feature to Shopify.Utils.graphqlProxy(ctx.req, ctx.res)
to handle case where the shop is authenticated under accessMode=offline
...
What inspired this enhancement?
Under the codebase of graphql_proxy.ts, the function calledloadCurrentSession
to load the current session. However, it didn't list out the third argument, which is a boolean type of whether the session is online or offline. And the default value is online.
Thus, no matter what type of accessMode the developers wanted, this function would only call loadCurrentSession with the online option, which would cause an error either not found the session or unauthorized if they used offline accessMode.
Currently, it works fine with the online mode, but it would be better to add an extra option for developers to choose the online/offline in order to usegraphqlProxy
easily.
...
Area: <area>
labels to this issuefrom shopify-node-api/docs/usage/webhooks.md
Note: The webhooks you register with Shopify are saved in the Shopify platform, but your handlers need to be reloaded whenever your server restarts. It is recommended to store your Webhooks in a persistent manner (for example, in a database) so that you can reload previously registered webhooks when your app restarts.
So what is it that needs to be stored? the webhook object that's returned here?
const response = await Shopify.Webhooks.Registry.register({
shop: config.shop,
accessToken: config.accessToken,
path: "/webhooks",
topic: 'APP_UNINSTALLED',
webhookHandler: async (topic, shop, body) =>{
appReducer(shop, {action: 'APP_UNINSTALLED'})
},
});
gid://shopify/WebhookSubscription/12345678901234
What do we do with this result?
Can you help update the documentation with a little more info about this?
...
What inspired this enhancement?
learning more about webhooks
...
Area: <area>
labels to this issueRight after migrate from old api to "Shopify.context" based api i started getting this error during attempt install embedded app
Immediately after changes have been made to the server.js, based on tutorial, i can't install app by /auth?shop=${shop} url, due to Error: Context has not been properly initialized. Please call the .initialize() method to setup your app context object
.env is completed by all data in accordance with docs and nice logs before start auth process
createShopifyAuth({
async afterAuth(ctx) {
Also Shopify.context logs ok during server starts. But /auth?shop=${shop} request crashes with above error.
package.json:
"dependencies": {
"@apollo/client": "^3.3.9",
"@google-cloud/storage": "^5.8.0",
"@shopify/app-bridge-react": "^1.29.0",
"@shopify/app-bridge-utils": "^1.29.0",
"@shopify/koa-shopify-auth": "^4.0.3",
"@shopify/polaris": "^6.0.1",
"@shopify/polaris-icons": "^4.5.0",
"@shopify/shopify-api": "^1.2.0",
"@zeit/next-css": "^1.0.1",
"dotenv": "^8.2.0",
"graphql": "^15.5.0",
"history": "^5.0.0",
"isomorphic-fetch": "^3.0.0",
"koa": "^2.13.1",
"koa-router": "^10.0.0",
"lodash": "^4.17.20",
"next": "^10.0.6",
"nodemon": "^2.0.7",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"request": "^2.88.2",
"store-js": "^2.0.4",
"uuid": "^8.3.2"
},
Can you advise something?
By choice we are not using Typescript. Is it compulsory to use this Module?
Follow in the documentation this does not work
const Shopify = require('@shopify/shopify-api');
const {API_KEY, API_SECRET_KEY, SCOPES, SHOP, HOST, PORT, APIVERSION, ISONLINE} = process.env;
Shopify.Context.initialize({
API_KEY,
API_SECRET_KEY,
SCOPES: [SCOPES],
HOST_NAME: HOST,
IS_EMBEDDED_APP: true,
API_VERSION: Shopify.ApiVersion[APIVERSION]
});
while this does
const Shopify = require('@shopify/shopify-api');
const {API_KEY, API_SECRET_KEY, SCOPES, SHOP, HOST, PORT, APIVERSION, ISONLINE} = process.env;
Shopify.default.Context.initialize({
API_KEY,
API_SECRET_KEY,
SCOPES: [SCOPES],
HOST_NAME: HOST,
IS_EMBEDDED_APP: true,
API_VERSION: Shopify.ApiVersion[APIVERSION]
});
Note the usage of Shopify.default. The same append for all methods, if default is not there node return this type of error: TypeError: Shopify.initialize is not a function
Is this because we are not using typescript and await syntax? How can we work with this module in plan JS?
The docs to begin OAuth don't include the isOnline
argument in the NodeJS code snippet. It is set in the Express example though and basically forces the Cookie to immediately expire when not set - i.e. breaking the OAuth flow when following the vanilla NodeJS docs. I would suggest using online auth in the NodeJS code as well.
I spent too much time reverse engineering why the vanilla NodeJS code wasn't working. This small adjustment in your docs could save others the time to figure it out the hard way.
...
Area: <area>
labels to this issueI'm trying to load the session via Shopify.Utils.loadCurrentSession(req, res, true)
but after reading the code I noticed it first checks if the request is valid with validateCallback
which then checks for the presence of a cookie called shopify_app_session
.
When I make a request to the server via the front-end the cookie is not sent because cookies are not allowed in Shopify Apps, thus cookies are empty and fails to load the session
object. Of course, if I set a shopify_app_session
cookie when the app is first loaded it works as expected, but it will be rejected due to the usage of cookies.
How do we solve this?
What I'm doing right now is returning the session
object from the validateCallback callback and then store this somehow e.g. accessToken and session ID in DB and shopOrigin in the client (sessionStorage, etc) for easy access. Is that the correct approach?
Hello,
What's the best way to handle token expiration with CustomSessionStorage (Redis or mongo or file-based) ? Do I just redirect with "/auth?shop={store_url}" ?
I'm trying to make an embedded app and use chrome browser.
validateAuthCallback-function throws ShopifyErrors.SessionNotFound.
/api/callback.ts
await Shopify.Auth.validateAuthCallback(request, response, query as AuthQuery);
SessionNotFound [Error]: Cannot complete OAuth process. No session found for the specified shop url: myshop.myshopify.com
sameSite: 'lax', missing cookies in callback request .
cookies.set(ShopifyOAuth.SESSION_COOKIE_NAME, session.id, {
signed: true,
expires: new Date(Date.now() + 60000),
sameSite: 'lax',
secure: true,
});
I've temporarily changed sameSite:'none',
and it works, but I don't know how to fix it correctly.
Write a short description of the issue here ↓
new Shopify.Clients.Graphql(shop, accessToken); is not working.
The code I am calling.
addScriptTag = () => {
const shop = "myshop.myshopify.com;
const accessToken = "shpat_xxx"
console.log("beforeClient", shop, accessToken)
const client = new Shopify.Clients.Graphql(shop, accessToken);
//error occurs when calling "Shopify.Clients.Graphql(shop, accessToken);"
// ...
}
// (I have added logs to the graphQL_client.js code)
function GraphqlClient(domain, token) {
// ...
console.log("graphql_client.js", domain, token)
// ...
this.client = new http_client_1.HttpClient(this.domain);
When I call addScriptTag(shop, accessToken), I receive the following logs:
beforeClient bobbetest.myshopify.com shpat_xxx
graphql_client.js shpat_xxx myshop.myshopify.com
InvalidShopError: Domain shpat_xxx is not valid. //this error is thrown in http_client.js since the access token is no valid url
...
graphql_client.js myshop.myshopify.com shpat_xxx
What do you think should happen?
Since my code is almost equal to the Instruction guide, and equal to the Shopify Node.js and React tutorial, I expected it to work.
What actually happens?
So as you can see, the GraphQl-client gets called twice. One time with the domain and accessToken parameter switched up, which causes the program to break. (see the two logs "graphQL_client.js ...") I think this is a bug not caused by my programming, since the log "beforeClient myshop.myshopify.com shpat_xxx" is only logged once.
I do not know how I should proceed to fix this bug.
the complete error message:
InvalidShopError: Domain shpat_xxx is not valid
┃ at InvalidShopError.ShopifyError [as constructor] (.../node_modules/@shopify/shopify-api/dist/error.js:13:28)
┃ at new InvalidShopError (.../webstory/node_modules/@shopify/shopify-api/dist/error.js:31:42)
┃ at new HttpClient (.../node_modules/@shopify/shopify-api/dist/clients/http_client/http_client.js:25:19)
┃ at new GraphqlClient (.../node_modules/@shopify/shopify-api/dist/clients/graphql/graphql_client.js:21:23)
┃ at _callee$ (.../server/handlers/mutations/addScriptTag.js:26:18)
┃ at tryCatch (.../node_modules/@babel/polyfill/node_modules/regenerator-runtime/runtime.js:63:40)
┃ at Generator.invoke [as _invoke] (.../node_modules/@babel/polyfill/node_modules/regenerator-runtime/runtime.js:293:22)
┃ at Generator.next (.../node_modules/@babel/polyfill/node_modules/regenerator-runtime/runtime.js:118:21)
┃ at asyncGeneratorStep (.../server/handlers/mutations/addScriptTag.js:8:103)
┃ at _next (.../server/handlers/mutations/addScriptTag.js:10:194)
┃ at .../server/handlers/mutations/addScriptTag.js:10:364
┃ at new Promise (<anonymous>)
┃ at .../server/handlers/mutations/addScriptTag.js:10:97
┃ at addScriptTag (.../server/handlers/mutations/addScriptTag.js:6:26)
┃ at _callee2$ (.../server/server.js:51:9)
┃ at tryCatch (.../node_modules/@babel/polyfill/node_modules/regenerator-runtime/runtime.js:63:40)
I am using koa-router for my api routes and I can't get the Webhook.process to work. It says that 'response.on' is not a function. What do I need to do to get this to work?
Register webhook:
const handleWebhookRequest = async (topic, shop, body) => {
console.log(this + '.hWhR() topic: ' + topic);
console.log(this + '.hWhR() shop: ' + shop);
console.log(this + '.hWhR() body: ' + body);
appUninstall(shop);
}
const resp = await Shopify.Webhooks.Registry.register({
path: '/api/admin/webhooks/app-uninstalled',
topic: 'APP_UNINSTALLED',
accessToken: token,
shop: shop,
webhookHandler: handleWebhookRequest
});
console.log('resp:');
console.dir(resp);
Api router callback:
const Router = require('koa-router');
const koaBody = require('koa-body');
const router = new Router({ prefix: routePrefix });
router.post('/app-uninstalled', koaBody(), async (ctx, next) => {
try {
await Shopify.Webhooks.Registry.process(ctx.request, ctx.response);
} catch (error) {
console.log(error);
}
});
Hello.
I have been trying for several days to use a custom session storage with the shopify CLI (Tried MongoDB, Redis, etc) without any success.
There's any repository with a proper custom session storage with shopify CLI that I can use as a reference?
Also anyone prefers shopify CLI over a scratch implementation? I've seen that shopify CLI adds various thing that could be a pain to debug in the future.
When you have an invalid or missing host
in App Bridge's react provider, it casues an infinite loop in JavaScript which ends up crashing the tab.
The console gets spammed with the same message:
Invalid config host must be provided page freeze
It should throw the error once or provide a way to handle the error and no infinite JavaScript loop should occur.
An infinite loop in JavaScript occurs causing the tab to crash.
Provider
from "@shopify/app-bridge-react"
.hello,
this lib does not handle errors or allows the user to handle them by himself.
for instance
RestClient does not load a valid json string? throws an error.
use case: if you try to load an asset that does not exist api will return a 404 status with no response body. which will break the .get() method. no way to read the actual response or header as far as i know
webhooks "register" method, the code assumes the response will for sure have a checkBody.data.webhookSubscriptions
when it doesn't it breaks of course with no way to debug.
Add keywords to package.json
to aid discoverability.
I want to use it like this.
if (!Shopify.Auth.verifyToken(req, res, "online") {
return res.redirect(`/auth?${req.query.shop}`);
}
Hi everyone!
I just started working on an embedded app with NodeJS and the documentation suggests I should use this package. The docs also say I must use App Bridge 2.0 on the frontend to generate session tokens that will be sent to the backend API.
Is this package updated to work with this new architecture? Checking the code, I see that it sets cookies to store the user session. Can I use this or should I implement my own logic until this package is updated?
Thank you very much
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.