Can't Fetch Graphql Admin API with Koa-shopify-graphql-proxy

Highlighted
Shopify Partner
6 0 3

Hey, 

I am trying to Fetch product details with the Graphql Admin API from my embedded app when a button click happens.

 

On my Server I am using Koa-shopify-graphql-proxy as middleware as per this tutorial

 

Server.js

const { default: graphQLProxy } = require('@shopify/koa-shopify-graphql-proxy');
const { ApiVersion } = require('@shopify/koa-shopify-graphql-proxy');


app.prepare().then(() => {
...
server.use(graphQLProxy({ version: ApiVersion.July19 }));
...
}

 

I am calling fetch as suggested here for client script -> https://github.com/Shopify/quilt/tree/master/packages/koa-shopify-graphql-proxy

As per my understanding, with this I can make a call from client and Koa-shopify-graphql-proxy will handle all the authentication on its end and should return the data.

 

Component.js

 

const GET_PRODUCT = gql`
  query {
      product (id: "gid://shopify/Product/#######"){
          title
          id
          description
          onlineStoreUrl
      }
  }
`;


handleProductSelection = (selectPayload) => {
....
fetch('/graphql', {method: 'POST' ,credentials: 'include', body: GET_PRODUCT})
.then(function(response) {
    return response.json();
})
.then(function(myJson) {
    console.log(JSON.stringify(myJson));
});
....
}
I am getting the following error when I run this. 

 

 

 

Component.js: POST https://#####.ngrok.io/graphql 400 (Bad Request)
Component.js: {"errors":{"query":"Required parameter missing or invalid"}}

 

In the docs of Koa-shopify-graphql-proxy they don't write 'method: POST'. If I remove then I get an error that can't send 'body' in GET/HEAD request.

 

What is the issue here?

  1. Query?
  2. How the fetch is composed? Or should I use some other fetch like isomorphic?
  3. My app is a sales channel app, is there something related to that?

 

Appreciate any help here guys.

 

Highlighted
Tourist
5 0 3

If you end up figuring it out please share your results. I'm also struggling with this. Cheers

Highlighted
Shopify Staff
Shopify Staff
1555 81 287

It looks to me like the body you're setting in your fetch request needs to instead be a JSON object. Requests to the GraphQL endpoint expect a JSON object with, at minimum, a query value. Here's what you can try:

 

fetch('/graphql', {method: 'POST', credentials: 'include', body: {"query": GET_PRODUCT}})

Let me know how that goes.

 

Cheers.

0 Likes
Highlighted
Tourist
5 0 3

 

Both: 

const body = {
      query: gql`
        query {
          shop {
            id
          }
        }
      `
    };

const response = await fetch('graphql', { method: 'POST', credentials: 'include', body: JSON.stringify(body) });

and

 

const body = {
    query: `
      query {
        shop {
          id
        }
      }
    `
  };

const response = await fetch('graphql', { method: 'POST', credentials: 'include', body: JSON.stringify(body) });

 

Return the same error: 

{"errors":{"query":"Required parameter missing or invalid"}} 

(Network Request Payloads below)

gql`QUERY`gql`QUERY``QUERY``QUERY` 

0 Likes
Highlighted
Shopify Staff
Shopify Staff
1555 81 287

Hmm, ok strange. Would you mind making an issue here?

 

Cheers.

0 Likes
Highlighted
New Member
1 0 1

You need the content-type: "application/json" header.

 

Here's a working example

 

const body = {
query: `query {
shop {
id
}
}`
};

export default {
getShop: async () => {
await fetch("graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify(body)
});
}
};

 

Highlighted
New Member
1 0 3

THIS WAS DESTROYING ME FOREVER. 

 

As soon as I got rid of the koa-bodyparser middleware, it worked.

 

Digging through the shopify-graphql-proxy.js in my node modules folder:

case 2:
                        if (accessToken == null || shop == null) {
                            ctx.throw(403, 'Unauthorized');
                            return [2 /*return*/];
                        }
                        console.log(ctx.request.body, accessToken);
                        return [4 /*yield*/, koa_better_http_proxy_1.default(shop, {
                                https: true,
                                parseReqBody: false,
                                // Setting request header here, not response. That's why we don't use ctx.set()
                                // proxy middleware will grab this request header
                                headers: {
                                    'Content-Type': 'application/json',
                                    'X-Shopify-Access-Token': accessToken,
                                },
                                proxyReqPathResolver: function () {
                                    return exports.GRAPHQL_PATH_PREFIX + "/" + version + "/graphql.json";
                                },
                            })(ctx, 
                            /*
                              We want this middleware to terminate, not fall through to the next in the chain,
                              but sadly it doesn't support not passing a `next` function. To get around this we
                              just pass our own dummy `next` that resolves immediately.
                            */
                            noop)];

Ignore the console.log(ctx.request.body, accessToken), that was something I added to the file.

 

The parseReqBody: false in the koa_better_http_proxy_1.default settings was the hint that I needed to disable the koa-bodyparser.

 

This is the query I used to finally get a 200 response and returned data in one of my served HTML files (I'm using Koa and serving HTML with the koa-ejs templating engine, no React):

 const scripts = (async function getScripts(){
        const GET_SCRIPTS = JSON.stringify({
        query: `query scriptTags { 
          scriptTags(first: 100){ 
            edges {
              node {
                src
                id
              }
              cursor
            }
            pageInfo {
              hasNextPage
            }
          }
        }`
        }); 
        console.log(GET_SCRIPTS);
        let res = await fetch('/graphql', {
          method: 'POST',
          credentials: 'include',
          headers: {
            'Content-Type': 'application/json'
          },
          body: GET_SCRIPTS,
        });
        let json = await res.json();
        console.log(json);
        return json;
      })();
      console.log(scripts);

I hope this helps :)

Highlighted
Shopify Partner
48 1 13

I encountered this scenario as well.


If you use a body parser, e.g. something like:

const bodyParser = require('koa-bodyparser');
...
server.use(bodyParser());

Then make sure the request to /graphql gets proxied with the unparsed body. Do something like:

server.use(async (ctx, next) => {
  if (ctx.path === '/graphql') {
    return await next();
  }
  await bodyParser()(ctx, next);
});

instead.

Just trying to help out.

We are web app developers, SEO ethousiasts, and do all kinds of other cool internet things.
Check us out at https://public-apps.com