How to use graphql on the front end

I have currently set up a route as follows:
import { RouteHandler } from "gadget-server";

const route: RouteHandler = async ({ request, reply, connections, logger }) => {
const shopify = connections.shopify.current;

if (!shopify) {
logger.error("No Shopify connection found.");
return reply.status(400).send({ error: "No shop found" });
}

try {
//logic where I use the connection to make a graphQL call to save settings in a metafield.
} catch (error) {
logger.error('Error saving settings:', error);
return reply.status(500).send({ error: 'Failed to save settings' });
}
};

export default route;
import { RouteHandler } from "gadget-server";

const route: RouteHandler = async ({ request, reply, connections, logger }) => {
const shopify = connections.shopify.current;

if (!shopify) {
logger.error("No Shopify connection found.");
return reply.status(400).send({ error: "No shop found" });
}

try {
//logic where I use the connection to make a graphQL call to save settings in a metafield.
} catch (error) {
logger.error('Error saving settings:', error);
return reply.status(500).send({ error: 'Failed to save settings' });
}
};

export default route;
And this is how I call it on my frontend react file:
const response = await fetch('/payment-customization-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(settings),
});
const response = await fetch('/payment-customization-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(settings),
});
However I keep getting this error:
app-bridge.js:1
POST https://hidesort--development.gadget.app/payment-customization-settings 500 (Internal Server Error)
(anonymous) @ app-bridge.js:1
await in (anonymous)
(anonymous) @ requests.js:1
(anonymous) @ PaymentCustomizationForm.tsx:155
onAction @ app-bridge.js:1
call @ app-bridge.js:1
f @ app-bridge.js:1

PaymentCustomizationForm.tsx:171
Error saving settings: Error: Failed to save settings
at PaymentCustomizationForm.tsx:164:15
app-bridge.js:1
POST https://hidesort--development.gadget.app/payment-customization-settings 500 (Internal Server Error)
(anonymous) @ app-bridge.js:1
await in (anonymous)
(anonymous) @ requests.js:1
(anonymous) @ PaymentCustomizationForm.tsx:155
onAction @ app-bridge.js:1
call @ app-bridge.js:1
f @ app-bridge.js:1

PaymentCustomizationForm.tsx:171
Error saving settings: Error: Failed to save settings
at PaymentCustomizationForm.tsx:164:15
Is there a better way to use the shopify client on the frontend in an embedded app instance
56 Replies
safwan
safwanOP8mo ago
OKay I tried using a global action and it is still giving me problems, I think this is happening mostly because of some authentication issues since the shopify client object is appearing as blank, how to fix this?
safwan
safwanOP8mo ago
okay but I'm not calling it from an extension, this is an embedded app, it is an authenticated instance, idk why its not working
safwan
safwanOP8mo ago
Yeah I did exactly whats in this guide to be precise, the section Calling Shopify within actions guide And im calling the global action like this in my code:
const [{ fetching: isSaving }, saveSettings] = useGlobalAction(api.savePaymentCustomization);
const handleSave = useCallback(async () => {
try {
await saveSettings({
payload: {
status: settings.status,
name: settings.name,
timing: settings.timing,
actions: settings.actions,
conditions: settings.conditions
}
});
app.saveBar.hide('payment-customization-form');
setIsDirty(false);
onBack();
} catch (error) {
console.error('Error saving settings:', error);
}
}, [settings, onBack, app.saveBar, saveSettings]);
const [{ fetching: isSaving }, saveSettings] = useGlobalAction(api.savePaymentCustomization);
const handleSave = useCallback(async () => {
try {
await saveSettings({
payload: {
status: settings.status,
name: settings.name,
timing: settings.timing,
actions: settings.actions,
conditions: settings.conditions
}
});
app.saveBar.hide('payment-customization-form');
setIsDirty(false);
onBack();
} catch (error) {
console.error('Error saving settings:', error);
}
}, [settings, onBack, app.saveBar, saveSettings]);
Someone please help me with this
Chocci_Milk
Chocci_Milk8mo ago
Hello, Could you please share the URL of the app and the relevant files? I'd like to take a closer look to assess the issue quickly
safwan
safwanOP8mo ago
hidesort.gadget.app the url api\actions\savePaymentCustomization.ts web\routes\new.tsx
Chocci_Milk
Chocci_Milk8mo ago
And where in the frontend are you making the request?
safwan
safwanOP8mo ago
in the routes\new.tsx line 89: const [{ fetching: isSaving }, saveSettings] = useGlobalAction(api.savePaymentCustomization);
Chocci_Milk
Chocci_Milk8mo ago
Oh, I misread, sorry
safwan
safwanOP8mo ago
ignore the PaymentCustomizationForm.tsx and PaymentCustomizationDashboard.tsx in components directory, they're not being used
Chocci_Milk
Chocci_Milk8mo ago
Would you allow me to make my own connection on your development environment so that I can test it on my end?
safwan
safwanOP8mo ago
oh for sure have at it no worries just help me fix this I've been sidequested on this stupid issue since the past 3 days almost
Chocci_Milk
Chocci_Milk8mo ago
How do I submit the form?
safwan
safwanOP8mo ago
just click save on the save bar ui
Chocci_Milk
Chocci_Milk8mo ago
Interesting. I don't have it
Chocci_Milk
Chocci_Milk8mo ago
No description
safwan
safwanOP8mo ago
weird it's working for me fine when i make changes
Chocci_Milk
Chocci_Milk8mo ago
Ohhh. I'm so used to the old app bridge save bar
safwan
safwanOP8mo ago
okay do you see it?
Chocci_Milk
Chocci_Milk8mo ago
Whoopsie
No description
Chocci_Milk
Chocci_Milk8mo ago
They should change that Its so hard to tell that something changed
safwan
safwanOP8mo ago
yup a little unintuitive but used to it now
Chocci_Milk
Chocci_Milk8mo ago
I wasn't able to get the !shopify statement to run. I'm getting Cannot read properties of undefined (reading 'currentAppInstallation')
safwan
safwanOP8mo ago
yup that's because the graphql query to get the app id is returning null howwver when i use the graphql explorer the exact same query does infact return the app id I think the problem is with the shopify client
Chocci_Milk
Chocci_Milk8mo ago
What API version do you use in the GraphiQL Playground? The Shopify client wouldn't make a difference since you're running graphql requests
safwan
safwanOP8mo ago
but the graphql requests are to the shopify api not the gadget api
Chocci_Milk
Chocci_Milk8mo ago
Oh, I thought you meant the shopify api node client It might be an error in the Shopify API. Do you mind checking the version used in the browser (GraphiQL UI)? It might be on the latest. They did have a ton of issues in 24-10
safwan
safwanOP8mo ago
okay wait lemme check
safwan
safwanOP8mo ago
okay so the api version here is 2025-01 and you can see the exact same query returns a valid response for the same app:
No description
Chocci_Milk
Chocci_Milk8mo ago
Can you try changing the API version to 24-10 to see if you get the same outcome?
safwan
safwanOP8mo ago
yup works just as well
No description
safwan
safwanOP8mo ago
the api version in my gadget app is however 24-10, how od i change that?
Chocci_Milk
Chocci_Milk8mo ago
You unfortunately can't move between API versions in Gadget. We also haven't yet released 25-01 yet
safwan
safwanOP8mo ago
oh makes sense
Chocci_Milk
Chocci_Milk8mo ago
Lets see what we can do to resolve this issue for now
safwan
safwanOP8mo ago
so hypothetically for the apps that are live on the store do I create a new app and clone it to upgrade the API version? and what's causing the issue here because I have followed the exact documentation to call global actions
Chocci_Milk
Chocci_Milk8mo ago
Sorry, I should have been more specific. You can't downgrade once you've went to a higher API version
safwan
safwanOP8mo ago
if you check the logs, the shopify connection is also returning a blank object but it isn't returning an error that it is null wither that's a little weird
Chocci_Milk
Chocci_Milk8mo ago
The reason we did this (currently) is because there are many moving parts to an upgrade/downgrade. Depending on the direction of the move we'd need to add or remove fields from your application. There's potential data loss and other issues that can happen. I think we have plans to add this in the future The Shopify connection isn't actually null/undefined. You'd get an error if you tried using shopify.graphql if that were the case. There's only an error in whats returned
safwan
safwanOP8mo ago
understandable, downgrading is not something I look forward to, its just that i want to keep my apps uptodate which isnt a prolem as of now
Chocci_Milk
Chocci_Milk8mo ago
Might I recommend that instead of immediately trying to destructure the return from the first call, you wait to see whats returned? That won't be an issue. Upgrades are one click
safwan
safwanOP8mo ago
check line 55 in the global action I try to output the instantiated shopify client:
logger.info('Shopify connection found:', JSON.stringify(shopify));
logger.info('Shopify connection found:', JSON.stringify(shopify));
This returns blank changed the code to get the entire returned field:
logger.info('Retrieved app installation ID', { currentAppInstallation });
logger.info('Retrieved app installation ID', { currentAppInstallation });
Still got this error:
error encountered running global action run function
action:savePaymentCustomization
Cannot read properties of undefined (reading 'currentAppInstallation')
TypeError: Cannot read properties of undefined (reading 'currentAppInstallation')
at run (/gadget/app/api/actions/savePaymentCustomization.ts:60:21)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at /app/packages/app-sandbox/src/EffectsImplementation.ts:173:18
at /app/packages/app-sandbox/src/EffectsImplementation.ts:159:16
at withConsoleLogger (/app/packages/app-sandbox/src/app-logger.ts:28:10)
at /app/packages/app-sandbox/src/EffectsImplementation.ts:157:12
at Object.runEffects (/app/packages/app-sandbox/src/EffectsImplementation.ts:201:43)
at /app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:103:32
at GlobalActionExecutor.maybeWithTransaction (/app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:157:7)
at GlobalActionExecutor.runImpl (/app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:100:7)
error encountered running global action run function
action:savePaymentCustomization
Cannot read properties of undefined (reading 'currentAppInstallation')
TypeError: Cannot read properties of undefined (reading 'currentAppInstallation')
at run (/gadget/app/api/actions/savePaymentCustomization.ts:60:21)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at /app/packages/app-sandbox/src/EffectsImplementation.ts:173:18
at /app/packages/app-sandbox/src/EffectsImplementation.ts:159:16
at withConsoleLogger (/app/packages/app-sandbox/src/app-logger.ts:28:10)
at /app/packages/app-sandbox/src/EffectsImplementation.ts:157:12
at Object.runEffects (/app/packages/app-sandbox/src/EffectsImplementation.ts:201:43)
at /app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:103:32
at GlobalActionExecutor.maybeWithTransaction (/app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:157:7)
at GlobalActionExecutor.runImpl (/app/packages/app-sandbox/src/services/GlobalActionExecutor.ts:100:7)
Chocci_Milk
Chocci_Milk8mo ago
Yes because its a big object. I would recommend instead logging like this:
logger.info({shopify}, "Shopify")
// OR
logger.info({graphql: shopify.graphql}, "...")
logger.info({shopify}, "Shopify")
// OR
logger.info({graphql: shopify.graphql}, "...")
But again, thats not the issue here
Chocci_Milk
Chocci_Milk8mo ago
Change this to something like the following code:
const appInstallationResult = await ...

logger.info({appInstallationResult})
const appInstallationResult = await ...

logger.info({appInstallationResult})
No description
Chocci_Milk
Chocci_Milk8mo ago
This needs to be switched around
No description
Chocci_Milk
Chocci_Milk8mo ago
logger.info doesn't behave like console.log You need to have the object as the first arguement
safwan
safwanOP8mo ago
umm okay so I did this:
const appInstallationResult = await shopify.graphql(`
query {
currentAppInstallation {
id
}
}
`);
console.log('App installation result:', { appInstallationResult });
const appInstallationResult = await shopify.graphql(`
query {
currentAppInstallation {
id
}
}
`);
console.log('App installation result:', { appInstallationResult });
`
Chocci_Milk
Chocci_Milk8mo ago
Do I have permission to change your code?
safwan
safwanOP8mo ago
Okay I got the error Just a minute I used console.log and voila I have the result: App installation result: { appInstallationResult: { currentAppInstallation: { id: 'gid://shopify/AppInstallation/536086937819' } } } SO that means the api is returning the app id correctly just some problem parsig you have permission to change my code but I think I got it
Chocci_Milk
Chocci_Milk8mo ago
Yep, the issue was hidden because you were destructuring
safwan
safwanOP8mo ago
i used console.log to see what the currentAppInstallation query returned
Chocci_Milk
Chocci_Milk8mo ago
You should use logger.info so that you have more performance and so that you have better structured logs. Just note that the message is always the second arguement and not the first
safwan
safwanOP8mo ago
alright got it sir Thank you so much for your help and assistance I am really so grateful to you you have no idea I cant believe I spent three days on this lol feeling kinda stupid ngl but regardless forever indebted to you man
Chocci_Milk
Chocci_Milk8mo ago
No problem!

Did you find this page helpful?