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

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.

2 Likes

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

3 Likes

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.

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)

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

Cheers.

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

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

3 Likes

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.

7 Likes