Can not make API calls to my app backend in the Admin UI extension

Hello, I am developing a embeded app with Admin UI extension.

I configured the application url to my backend server with a ngrok domain (https://xxxx.ngrok-free.app).
I followed the doc to make fetch calls to my backend api on the same ngrok domain. I both tried full url and relative path to the target api.

const headers = new Headers({ “ngrok-skip-browser-warning”: “69420” });

const r1 = await fetch(‘https://xxxx.ngrok-free.app/backendApi/variant’,
{
method: “get”,
headers: headers,
}
);

const r2 = fetch('/backendApi/variant',
  {
    method: "get",
    headers: headers,
  }
);

It was quite strange that I could not see any request sent out the the target on chrome devtool.

But when I modified the domain name to a different one with the configuration’s, I could see the request was sent out with a CORS error on chrome devtool.

Is there anyone who can help me solve this problem?

Thanks.

1 Like

It only allows me to post 2 links in a topic, so I post the doc link and the the code to call a different domain here.

Here is the doc I followed. Admin UI extensions

Use the following code I can see the request was sent out with a CORS error on chrome devtool.
const headers = new Headers({ “ngrok-skip-browser-warning”: “69420” });

const r1 = await fetch(‘https://yyyy.ngrok-free.app/backendApi/variant’,
{
method: “get”,
headers: headers,
}
);

Hi @Allan_Zhang

CORS is a security feature to prevent cross origin requests, meaning the JavaScript on the domain (in this case *.shopify.com since it’s coming from an Admin Extension) doesn’t match your own API’s domain.

You’ll need to configure your server’s application code to accept requests from *.shopify.com in order to allow browser side API requests from the Shopify domain.

This is a security feature built into browsers. You’ll see an OPTIONS request that’s failing in the browser’s network tab. That’s the CORS request checking to see if your API accepts CORS requests from the current domain.

There’s middleware packages for pretty much every framework out there that help you configure CORS. Could you share more about your API’s stack? Is it Node.js? Ruby on Rails?

Thanks Dylan,

Actually I used the same domain with the application url configured for my application backend, as the doc says.

But I can not see any request sent out to the target in chrome devtool. (for both using full path or relative path)

I changed to another domain is for trouble shooting to verify if the fetch works in the extension.

So my current situation is that the fetch calls to the api in the same domain with application url can not be sent out, however, the calls to a different domain can be sent out with CORS error. But I would like to use the same domain to my app backend as the docs says.

I use the default setting to create the App and Extension,
Here are the steps:

shopify app init
select “Remix app”
select “javascript”

shopify app generate extension
select “Admin action”

All of these steps used default value.

@Allan_Zhang

Yes, Shopify’s fetch modifications will allow you to send the auth token with the request automatically, it will even include the host name for you for relative pathing.

But it does not and cannot handle CORS configuration for you.

CORS configuration != Authentication with session tokens.

Potentially you have a filter on the Network tab in your browser’s Network dev tools that’s only including GET/POST requests. This request is an OPTIONS request that is failing.

Feel free to share your backend framework & language for specific middleware examples on how to configure CORS. It’s a minor fix but yes, it’ll block you from making requests without proper set up.

Thanks, Dylan.

I used java springboot + vue, running on tomcat and nodejs. I have a proxy setting for path /backendApi and set changeOrigin as true, just like the following.
‘/backendApi’: {
target: ‘http://localhost:8443’,
changeOrigin: true,
xfwd: true,
}

I also used a filter in java side which allows CORS.
corsConfiguration.addAllowedOrigin(““);
corsConfiguration.addAllowedHeader(”
”);
corsConfiguration.addAllowedMethod(“*”);

And I also used ngrok to expose my backend service could be accessed from the internet.

Thanks for clarify that the OPTIONS request is excluded by the dev tools.

But it is really strange that if I configure the application url to a different one (no matter what it is, just a fake url),
I can make the call correctly to my backend service with the following code.

const res = await fetch(‘https://4b73-96-49-244-81.ngrok-free.app/backendApi/consignment/variant’, {
method: “POST”,
body: JSON.stringify(postData),
headers: headers,
mode: ‘cors’,
});

once I change the application url to real one (the same with the url in the code), it can not work.

I mean in the shopify.app.toml configuration file.

Perhaps Ngrok is modifying CORS on your behalf, and when you use your production version the application isn’t setting the correct CORS headers?

You can double check by viewing only OPTIONS requests in your dev tools and see the response from your server. If OPTIONS request fails and the CORS policy is rejecting the *.shopify.com domain, then most likely the configuration of your backend service is the problem.

I suspect your proxy is most likely the issue. The proxy might be modifying the CORS headers after the Java application is setting them.

I think it seems like something is wrong with the configuration for the remix template.

As the doc says, when making fetch() requests to the app’s configured auth domain (app_url), it will add shopify ID token automatically. Therefore, there should be an interceptor to deal with this, including resolving the relative URLs and adding ID token.

If I am not using the domain in the app_url, it will bypass this part, then the fetch requests can be sent out. (I can see both OPTIONS and POST request in the backend)

If I use the domain in the app_url or relative path, the interceptor will start to work, but fails (maybe due to my configuration), then it stops to send the request out.

However, the doc is pretty simple, I am not sure if there is something I missed.

And I could not get the ID Token using the following code:

console.log(‘call backend api here’);
console.log(postData.payout);

console.log(auth);

try {
  console.log('to get id token.')
  const token = await auth.idToken();
  console.log(token);
} catch (error) {
  console.error(error);
}

I suspect the remix template also fails to get ID token when it finds the api target is in the same domain with the app url and trys to add ID token headers.

I recreate a clean extension and it works now. Thanks for your help.

1 Like

@Allan_Zhang We’re encountering the same CORS issue and have been troubleshooting it for two days without success. We’ve followed the documentation meticulously, tested various CORS headers, but nothing seems to work.

Which language did you use to rebuild your extension? Was it JavaScript (React/Vanilla) or TypeScript (React/Vanilla)?