A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
I'm building off the Shopify CLI Node + React starter app using App Bridge and Apollo to do GraphQL queries.
No matter what I try, I get 400 bad request.
I have no problems querying the REST Admin API and receiving responses.
But when I try to use GraphQL, it's always 400 bad request.
Just like the ProductsCard component in the starter app built by Shopify CLI, I've created an OrdersCard component which I want to use to display the most recent 5 orders (later I will be filtering for a specific cart attribute, but for now, I am just trying to get this working, so keeping the query simple.
Here's the code in my ProductsCard component:
const ORDERS_QUERY = gql`
query getOrders {
orders {
edges {
node {
id
}
}
}
}
`;
const { loading, error, data, refetch } = useQuery(ORDERS_QUERY);
if (loading) {
console.log('loading...');
return <Loading />;
}
if (error) {
console.log('error trying to get orders: ', error);
return (
<Banner status="critical">There was an issue loading orders.</Banner>
);
}
In App.jsx, I'm using the default ApolloProvider setup built by the Shopify CLI:
function MyProvider({ children }) {
const app = useAppBridge();
// getSessionToken(app).then(token => {
// console.log('getSessionToken result: ', token)
// });
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
credentials: "include",
fetch: userLoggedInFetch(app),
// headers: {
// 'Content-Type': 'application/graphql',
// },
}),
});
return <ApolloProvider client={client}>{children}</ApolloProvider>;
Finally, here's what App function returns in App.jsx, so as you can see, MyProvider is wrapped around any page content:
<BrowserRouter>
<PolarisProvider i18n={translations}>
<AppBridgeProvider
config={{
apiKey: process.env.SHOPIFY_API_KEY,
host: new URL(location).searchParams.get("host"),
forceRedirect: true,
}}
>
<MyProvider>
<AppNavigation />
<PageLayout>
<Switch>
<Route path="/settings" component={Settings} />
<Route path="/" component={Dashboard} />
</Switch>
</PageLayout>
</MyProvider>
</AppBridgeProvider>
</PolarisProvider>
</BrowserRouter>
Solved! Go to the solution
This is an accepted solution.
Another update.. I have given up on using Apollo to query graphql from my React component.
I think the query is not actually being sent to the backend to be processed there, and I was misunderstanding the /graphql endpoint I see in the node server index.js file and that's not related.
So at this point, my assumption is that I've been trying to query the GraphQL Admin API directly from the frontend, unlike how I thought. So that's why I kept receiving 400 bad request errors saying that it cannot query field "orders" on type "Query"...
My solution was to abandon using Apollo/App-Bridge for this, and just reuse what I found in the Shopify CLI node starter app's verify-request.js. Example below:
import { Shopify } from "@shopify/shopify-api";
const TEST_GRAPHQL_QUERY = `
{
shop {
name
}
}`;
const client = new Shopify.Clients.Graphql(
session.shop,
session.accessToken
);
const response = await client.query({ data: TEST_GRAPHQL_QUERY });
if (response) {
//do the stuff you want to do with the response data
}
Just for very basic testing with no flexibility, since I'm trying to get the last 10 orders, I added this endpoint to my node server index.js:
app.get("/orders-list", verifyRequest(app), async (req, res) => {
const session = await Shopify.Utils.loadCurrentSession(req, res, true);
console.log('===================================================');
console.log('trying to run /graphql/admin-api stuff in index.js');
try {
const client = new Shopify.Clients.Graphql(
session.shop,
session.accessToken
);
const ORDERS_QUERY = `
{
orders(first:2) {
edges {
node {
id
}
}
}
}`;
const response = await client.query({ data: ORDERS_QUERY });
if (response) {
console.log('checkOrders================== : ', response);
}
res.status(200).send(response.body);
} catch (error) {
res.status(500).send(error.message);
}
Then in my React component, OrdersList.jsx, I fetch that endpoint and receive the data back to do what I want with...
const app = useAppBridge();
const fetch = userLoggedInFetch(app);
async function getOrdersList() {
const orderData = await fetch("/orders-list").then((res) => res.json());
if (orderData) {
setOrdersList(orderData);
console.log("orders list: ", orderData);
}
return orderData;
}
useEffect(() => {
getOrdersList();
}, []);
Hey @buildpath - I took a quick look at your GraphQL query and I think I may have an answer for why you're getting that 400 error.
You'd need to set some parameters in your query so that we can return a paginated list of them in a response call. Essentially, imagine your store has 10,000 orders, it would be too much to return in an API response, so we use parameters like "first" or "last" in order to return a more specific amount of results in the API response. There's a bit more about how this works in our GraphQL documentation and I also wanted to share an example query if you wanted to try this syntax to see if it works and returns a successful response in your app:
query getOrders {
orders {
edges {
node {
id
}
}
}
}
Hope this helps and gives you some next steps for building your app.
Cheers!
Alan | API Support @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hi Alan, thanks for your reply!
The query you shared seems to be exactly the same as the one I am already using. Did you mean to modify it? Regardless, just in case I missed something, I copied and pasted your query, and I still get 400 error. I really wish I could get more information than just "Bad request" such as syntax error details, for example.
Could you share a query example that includes pagination like you said is required?
I had previously tried the below query but it also didn't work:
query getOrders {
orders(first: 2) {
edges {
node {
id
}
}
}
}
No worries! Thanks for catching that - that typo is on me! The query you shared in your second reply here is closer to what I had meant to originally share! Can you try something like this and let me know if this works?
query getOrders {
orders(first:2) {
edges {
node {
id
}
}
}
}
The only part of the last query you shared that I could see returning that 400 error is the space between "first" and "2". Let us know if that works!
Alan | API Support @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hey Alan, no worries! Thanks for confirming. I tried this query and still get error 400.
I've also tried without naming the query, in case that was somehow an issue. But still same error.
query getOrders {
orders(first:2) {
edges {
node {
id
}
}
}
}
Is it possible for me to acquire any more detail from Apollo/Graphql besides 400 bad request? Or is that truly all it provides in the error response?
Thanks for trying that - definitely odd! I was able to replicate the GraphQL query using our GraphiQL app in my test environment and it returned successfully with the list of orders, so the query itself doesn't seem to be the issue. In terms of the error code you're seeing in the response, my understanding is that it would only surface the 400 error without additional details, but if you're able to source a Request ID (should show as an "x-request-id" field in the raw header response) returned through the GraphQL response headers, I definitely be happy to check the logs on on our end to see if we can see what happened when the request went out to the GraphQL API endpoint on our end.
If you've set the app itself up through your Partner Admin/Dashboard, I could also use the app's ID if you can't retrieve the request ID - either/both of those would be helpful to have on our end so we can take a look at this.
It is definitely odd that the query isn't working on your end. We can't provide solutions or too much advice when it comes to building apps as the API Support team isn't a development team - but I'm more than happy to keep working on this with you to see if we can at least figure out what's causing the error here!
Alan | API Support @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Alan, I'm glad you asked me to double check the response headers.
I thought I was getting no response because the last time I checked this in dev tools network panel, the graphql 400 error response tab showed that there was no response because the connection was closed.
However, I checked just now, and the response is more helpful! I still don't know what the solution is though.
The error is GRAPHQL_VALIDATION_FAILED
"message": "Cannot query field \"orders\" on type \"Query\".",
Here's the payload in the request:
{
"operationName": "getOrders",
"variables": {},
"query": "query getOrders {\n orders(first: 2) {\n edges {\n node {\n id\n __typename\n }\n __typename\n }\n __typename\n }\n}"
}
Glad I was able to help here, at least a little bit! Very odd, generally, when it comes to our APIs, that "cannot query field... on type..." error pops up when you're trying to query a GraphQL Schema that doesn't contain those fields. Just to clarify - are you wanting to query the Storefront API or the Admin API? I don't believe the Storefront API contains the "orders" object as a queryable field (just "order", so this could potentially be why we're seeing the error here.
Alan | API Support @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Alan, I was researching the error info as well and came to the same conclusion. It seems I'm querying the GraphQL Storefront API instead of the Admin API. However, I don't see where I'm specifying the API I'm trying to query. I had just assumed that the App Bridge and ApolloServer set up by the default CLI node app was going to query the Admin API.
I'm clearly missing something. I have not been able to find a solution (to specify querying the admin api) in documentation so far.
Edit: In case it matters, I also still have this default Shopify CLI node app code in my /server/index.js to handle POSTs to /graphql. I see that according to react-apollo's readme, react-apollo defaults to POSTing graphql queries/mutations to /graphql endpoint. Is there something in the code below that is causing me to query Storefront instead of Admin API?
app.post("/graphql", verifyRequest(app), async (req, res) => {
try {
const response = await Shopify.Utils.graphqlProxy(req, res);
res.status(200).send(response.body);
} catch (error) {
res.status(500).send(error.message);
}
});
Update... now I'm even more confused...
In this official Shopify tutorial, it shows how to retrieve products using the GraphQL Admin API and display them in a list in the app. This is exactly the same structure I'm using to try to query GraphQL Admin API to get a list of recent orders. I simply repurposed the code from the tutorial and changed the query.
So I'm struggling to discern how querying orders doesn't work but querying products does work, if both are querying the Admin API (remember this is the Shopify CLI node starter app I'm working with
This is an accepted solution.
Another update.. I have given up on using Apollo to query graphql from my React component.
I think the query is not actually being sent to the backend to be processed there, and I was misunderstanding the /graphql endpoint I see in the node server index.js file and that's not related.
So at this point, my assumption is that I've been trying to query the GraphQL Admin API directly from the frontend, unlike how I thought. So that's why I kept receiving 400 bad request errors saying that it cannot query field "orders" on type "Query"...
My solution was to abandon using Apollo/App-Bridge for this, and just reuse what I found in the Shopify CLI node starter app's verify-request.js. Example below:
import { Shopify } from "@shopify/shopify-api";
const TEST_GRAPHQL_QUERY = `
{
shop {
name
}
}`;
const client = new Shopify.Clients.Graphql(
session.shop,
session.accessToken
);
const response = await client.query({ data: TEST_GRAPHQL_QUERY });
if (response) {
//do the stuff you want to do with the response data
}
Just for very basic testing with no flexibility, since I'm trying to get the last 10 orders, I added this endpoint to my node server index.js:
app.get("/orders-list", verifyRequest(app), async (req, res) => {
const session = await Shopify.Utils.loadCurrentSession(req, res, true);
console.log('===================================================');
console.log('trying to run /graphql/admin-api stuff in index.js');
try {
const client = new Shopify.Clients.Graphql(
session.shop,
session.accessToken
);
const ORDERS_QUERY = `
{
orders(first:2) {
edges {
node {
id
}
}
}
}`;
const response = await client.query({ data: ORDERS_QUERY });
if (response) {
console.log('checkOrders================== : ', response);
}
res.status(200).send(response.body);
} catch (error) {
res.status(500).send(error.message);
}
Then in my React component, OrdersList.jsx, I fetch that endpoint and receive the data back to do what I want with...
const app = useAppBridge();
const fetch = userLoggedInFetch(app);
async function getOrdersList() {
const orderData = await fetch("/orders-list").then((res) => res.json());
if (orderData) {
setOrdersList(orderData);
console.log("orders list: ", orderData);
}
return orderData;
}
useEffect(() => {
getOrdersList();
}, []);