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

Shopify Partner
6 0 1

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.

 

1 Like
Highlighted
Tourist
5 0 2

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

2 Likes
Highlighted
Shopify Staff
Shopify Staff
1555 80 256

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 2

 

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 80 256

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)
});
}
};

 

1 Like
Highlighted
New Member
1 0 2

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 :)

2 Likes
Highlighted
Shopify Partner
36 1 5

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
2 Likes