Multiple Queries in a Single GraphQL Request
A common pattern I've seen in React/GraphQL applications is one where multiple queries are run at the top of a component (Here using useQuery
from apollo-client)
export const MyComponent = () => {
const { data: data1, error: error1, loading: loading1 } = useQuery(query1);
const { data: data2, error: error2, loading: loading2 } = useQuery(query2);
if (loading1 || loading2) return <Loading />
else if (error1 || error2) return <Error />
else return (
…
)
}
The worst I've seen so far is a component with 6 queries and a correspondingly tangled web of spaghetti logic below it to deal with the permutations of queries being in a loading, error, or data-ready state.
TDLR: Don't do this!
GraphQL has a feature that has been hiding in plain sight for years - the ability to execute multiple queries in a single request!
Why is this desirable?
Fewer network requests
Simpler code
Example 1: two requests, no parameters
The following GraphQL query shows a query that includes two separate queries.
const TWOQUERIES = gql`query twoQueries {
query1 {
field1
field2
…
}
query2 {
fieldA
fieldB
…
}
}
`;
Here twoQueries
is a name chosen by the UI developer while query1
and query2
are specified in the typeDefs on the server.
With this combined query defined, the React component is simplified:
export const MyComponent = () => {
const { data, error, loading } = useQuery(TWOQUERIES);
if (loading) return <Loading />
else if (error) return <Error />
else return (
…
)
}
Example 2: two requests with parameters
query twoQueries($p1: string!, $p2: string!) {
query1(p1: $p1) {
field1
field2
…
}
query2(p2: $p2) {
fieldA
fieldB
…
}
}
Note how the combined query twoQueries
needs to accept all the parameters for all of the queries it includes.
Example 3: The same query twice with different parameters
Sometimes you just want two or three of something but the server doesn't have a query that accepts an array of parameters and returns an array of results. That's ok, you can use the same query multiple times, you just have to give each instance a unique alias.
query sameQueryTwice($p1: string!, $p2: string!) {
q1:myQuery(p: $p1) {
field1
field2
…
}
q2:myQuery(p: $p2) {
field1
field2
…
}
}
Note that, unlike javascript where the alias comes after the original name, in GraphQL queries, the alias name comes before the original name.
Deconstructing the Results
useQuery
returns a data
object that has the names of the queries as keys. There is no real difference between a request with a single query and one with multiple queries.
const { myQuery } = data; // single query
const { query1, query2 } = data; // multiple queries
Chaining Queries
A question that I've seen several times is how to chain GraphQL queries, i.e. how to make the second query depend on the results of the first. Unfortunately, there is no way to do that when queries are combined into a single request - the server will receive both queries at the same time, run them independently, and return both results. If your use case requires that the results of one query be used by a subsequent query then you will indeed have to run two separate queries in sequence (not in parallel).
In practice, I've rarely found a case for chained queries unless there's a user action in between. For example, the user selects an item from a list and then a second query is run to get additional details about that item. Thoughtful schema and query design can mitigate the need for chained queries.
I hope you find this useful. For more GraphQL content please see my Hashnode blog.