Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidRequestError: Request does not contain a host query parameter #1234

Open
NomanHameed opened this issue Feb 14, 2023 · 14 comments
Open

Comments

@NomanHameed
Copy link

I'm creating a Shopify app using node with PostgreSQL my app working fine on local with Ngrok but when i deploy on Heroku on calling app with shop it returns error.

[shopify-app/INFO] Running ensureInstalledOnShop
/app/web/node_modules/@shopify/shopify-api/lib/error.js:13
var _this = _super.apply(this, tslib_1.__spreadArray([], tslib_1.__read(args), false)) || this;
^

InvalidRequestError: Request does not contain a host query parameter
at InvalidRequestError.ShopifyError [as constructor] (/app/web/node_modules/@shopify/shopify-api/lib/error.js:13:28)
at new InvalidRequestError (/app/web/node_modules/@shopify/shopify-api/lib/error.js:225:42)
at /app/web/node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.js:28:31
at step (/app/web/node_modules/tslib/tslib.js:193:27)
at Object.next (/app/web/node_modules/tslib/tslib.js:174:57)
at fulfilled (/app/web/node_modules/tslib/tslib.js:164:62)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

my root package.son dependencies

"dependencies": {
    "@shopify/app": "3.43.0",
    "@shopify/cli": "3.43.0",
    "shopify": "^0.1.0"
  },

route calling

https://{shopurl}.herokuapp.com/?shop={shopname}myshopify.com

@jamalsoueidan
Copy link

Did you figure it out?

@mkevinosullivan
Copy link
Contributor

mkevinosullivan commented Mar 13, 2023

Access the app from the store (if installed), or from the partner account (if not yet installed on a store). The Shopify system adds a host query parameter when calling the app. Locally, the CLI adds the host query parameter.

@jamalsoueidan
Copy link

With the hooks they provided, the shop is added to the query parameter, but I don't see where they add the host in the hook?

@mkevinosullivan
Copy link
Contributor

To which hooks are you referring?

@jamalsoueidan
Copy link

jamalsoueidan commented Mar 17, 2023

I think you mean when going to the app first time the host is added in the url?

I'm getting that error after I call the api from the frontend.

@mkevinosullivan
Copy link
Contributor

What hooks are you using to call the API from the frontend? Also, what version of App Bridge is being used?

@NomanHameed
Copy link
Author

I think you mean when going to the app first time the host is added in the url?

I'm getting that error after I call the API from the front-end.

first time call issue is resolve but in second condition "when I'm calling the API from the front-end" you are right I'm also facing same issue due to Shopify middle-ware

app.use("/*", shopify.ensureInstalledOnShop(), async (_req, res, _next)

currently resolve by bypassing this route middle-ware any better solution for this issue please guide.

@bashunaimiroy
Copy link

bashunaimiroy commented Apr 27, 2023

Update: Here's why this might be happening and how to fix it

tldr: your routes might not be completely handling your request, so it continues by default to the ensureInstalledOnShop middleware, even if you don't actually need it for that request.

Let's say for example, you are trying to send a fetch request from your frontend to /api/blah. It doesn't have any query params on it: not host, not shop, not &embedded=1, nothing. You're getting an error from ensureInstalledOnShop that says it expects one of these parameters on the request.

This may be happening because of how your route is set up. You need to make very sure that you have set up your Express App in the following ways:

  1. Set up a route that will respond to that path. E.g. app.post('/api/blah', (req, res) => {}).
  2. The callback/handler for that route should return a response, e.g. res.status(200).send('Hello World'), and not pass the request onwards using next(). Look at the example handlers in the template for reference.
  3. This route must appear in your code above the ensureInstalledOnShop middleware, i.e. app.use("/*", shopify.ensureInstalledOnShop().

The reasons: When your Express Server receives the request, if you don't have a route set up, or if that route passes it onwards instead of returning a response, then the ensureInstalledOnShop middleware will be applied- since it gets applied to all requests matching "/*" path.

Similarly, if you added your route below that middleware, it would be too late, because the middleware would have already been applied.

Anytime the ensureInstalledOnShop middleware gets applied, and you don't have a host parameter on your request, this problem will occur.

I was experiencing this problem simply because I was still adding routes, debugging them as I went along, and I didn't realise the above requirements. I might make a PR to add clarifying comments to the Express template, because as it's currently set up, it's not clear what is going wrong.

So, if you want to fix this, check that your route is set up properly. If everything's set up right, the request will be caught, handled, responded to, and the middleware will never get applied.

Original Post

I originally posted a fix, to add the host parameter. It might still be helpful if the above reason doesn't help you.

Hi, I'm a fellow dev working on a Shopify App, who's also experiencing this after creating an app from shopify-cli 3.45.1.

I have a solution, even though I don't understand why it's necessary. The solution involves modifying the frontend code to add the host parameter. I'm basing the solution on the React Frontend that the CLI creates- the source code is here. It could be applied to other frontends, though.

I didn't have to remove the ensureInstalledOnShop middleware, although I completely understand why you did that, it's frustrating.

Instead, I modified the web/hooks/frontend/useAuthenticatedFetch.js file, which contains a function called useAuthenticatedFetch. That function is (as the name suggests) meant to allow you to make authenticated fetch requests from the frontend to the backend.

In that function, I added some code that will add the host parameter to the fetch request that goes out. Since it accepts a uri argument of type string, which is a relative URL, like "api/graphql" in my case, we can set the URL Params by doing some string manipulation to it. Here's the code I used:

function setUrlParams(url, key, value) {
  const urlSplit = url.split('?');
  const usp = new URLSearchParams(urlSplit[1]);
  usp.set(key, value);
  urlSplit[1] = usp.toString();
  return urlSplit.join('?');
}

and here's the new useAuthenticatedFetch function:

export function useAuthenticatedFetch() {
  const app = useAppBridge();
  const fetchFunction = authenticatedFetch(app);

  return async (uri, options) => {
    const currentHostValue = window.__SHOPIFY_DEV_HOST;
    const uriWithHostParam = setUrlParams(uri, 'host', currentHostValue);

    const response = await fetchFunction(uriWithHostParam, options);
    checkHeadersForReauthorization(response.headers, app);
    return response;
  };
}

Note that in my app, the host value is coming from a global variable: window.__SHOPIFY_DEV_HOST. this window.__SHOPIFY_DEV_HOST variable is set in AppBridgeProvider.jsx. If your app does not have this provider (e.g. because it's not embedded), you may have to get the host value from elsewhere in your app. Good luck.

Disclaimers:

  • I do not work for Shopify
  • I didn't build their template
  • There may be a good reason not to use this fix.

@yaizudamashii
Copy link

Unfortunately neither of @bashunaimiroy 's solution worked for me.

I have another theory. When I console logged shop and host, host has a whitespace. So maybe ensureInstalledOnShop is not able to handle whitespaces. Why did the host have a whitespace in the first place? I have no idea.

If you are developing a Shopify app, I'm sure that you are using a session storage. Simply delete the entry that corresponds to your test shop, and Shopify will create a new host and session. The new host did not have a whitespace, so the app under development ran fine for me after the deletion of the previous session.

@yaizudamashii
Copy link

Ok I spoke too soon, every subsequent request after generating a new session host fails and has a whitespace. Something is happening but if you delete the session every time it works... obviously not a permanent solution.

@xegulon
Copy link

xegulon commented May 3, 2023

@bashunaimiroy after applying your two fixes, I got this error in my dev environment when trying to fetch with the authenticated fetch:

Access to fetch at 'https://admin.shopify.com/store/test-shop-XXXX/apps/4650946...30ff462ca/' 

(redirected from 'https://played-XXXXXecial-bk.trycloudflare.com/api/reviews?host=YW...XRlc3Qtc2F0b3I')

from origin 'https://played-waXXXXXk.trycloudflare.com' has been blocked by CORS policy: 

Response to preflight request doesn't pass access control check: 

No 'Access-Control-Allow-Origin' header is present on the requested resource. 

If an opaque response serves your needs, set the request's mode to 'no-cors' to 
fetch the resource with CORS disabled.

Shopify please have a look 😿

@fidotheprince
Copy link

Okay here is the work around I found,

When making a request on the frontend of your application to the backend, template the value for your host into the requested url. Below is an example via Shopify's authenticated fetch method.

  const fetch = useAuthenticatedFetch();

  //fetch data points data from API using the fetch function;
  const onSubmit = async () => {
    
    //host is added here to bypass shopify.ensureInstalledOnShop() in the backend;
    const host = `host=${window.__SHOPIFY_DEV_HOST}`
    const url = `/api/points?${host}`;
    const method = 'GET';

    const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' } });

    if (response.ok) {
      const data = await response.json();
      console.log(data);
    } 
  };

@dani-sanomads
Copy link

@mkevinosullivan any thoughts. I'm using tanStack with useAuthenticatedFetch() method and app-bridge-3.1.0. When i open the app using partner account or from the store during development it logs this for any backend hook call :- [shopify-app/ERROR] ensureInstalledOnShop did not receive a shop query argument | {shop: undefined}
I checked for the shop and host in the req.query and it was undefined after session validation although the session is valid and updated in the DB:-
app.use("/api/*", shopify.validateAuthenticatedSession());

Everything was working fine on 28th June, 2023. Are there any updates regarding this issue ?

@dani-sanomads
Copy link

I have updated the shopify/cli and shopify/app to 3.47.2 and also updated the react-router-dom to 6.14.1 and it worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants