I’m trying to combine multiple GraphQL queries into one query using JavaScript.
I am looking for something like this:
let query3 = mergeQueries(query1, query2);
We won’t know beforehand which queries will be combined.
Suppose I have queries like this:
input query1:
{
post(id: 1234) {
title
description
}
}
input query2:
{
post(id: 1234) {
tags
author {
name
}
}
}
Then I would like the result query3 to be:
result query3:
{
post(id: 1234) {
title
tags
description
author {
name
}
}
}
This would be the same functionality as lodash _.merge()
does for JSON objects, but then with GraphQL queries instead of JSON objects.
2
5 Answers
My answer is getting a bit to long for a comment so I decided to write a direct answer here. First I think both comments are really helpful. There is a solution that let’s two queries share an HTTP request which might already be the optimisation you are looking for. Furthermore merging queries is not trivial. It requires a lot of effort to do this down to a field level. Not only fragments can make this difficult, you also have to take variables into account. As far as I know there is no solution publicly available to do that so you would have to do it yourself. Also I have not heard of a company that does this. I think that the fact that there is no solution is an indicator that it might not be worth it to do it.
I can only guess your problem, but another way to reduce the amount of queries sent by a frontend application is to make use of fragments. While your fragments cannot have variables a healthy component structure will still fit very well with fragments:
fragment PostHeader on Post {
title
description
}
fragment PostMeta on Post {
tags
author {
name
}
}
query {
post(id: 1234) {
...PostHeader
...PostMeta
}
}
2
-
This looks like a very useful solution in many cases. I expect a lot of overlap between the input queries which will make this solution less ideal, so I will look into building my own merge function.
– Hendrik JanFeb 19, 2019 at 7:00
-
1
We wrote some code for combining fragments, with automatic naming of the fragments and removing duplicate fragments, and released it here: github.com/SVT/graphql-defragmentizer
– Anders KindbergAug 23, 2019 at 7:19
Thanks to parameterized fragments you can take variables into account! Assuming post
is a field of the root query type the combined query referring to the above example would be:
fragment PostHeader on RootQueryType {
post(id: $id) {
tags
author {
name
}
}
}
fragment PostMeta on RootQueryType {
post(id: $id) {
tags
author {
name
}
}
}
# ID being the id type
query($id: ID! = 1234) {
...PostHeader
...PostMeta
}
or rather in a real-world scenario you’d be passing in the id dynamically (e.g. in your post request), see: https://graphql.org/learn/queries/#variables
I wrote a lib for this: https://github.com/domasx2/graphql-combine-query
import comineQuery from 'graphql-combine-query'
import gql from 'graphql-tag'
const fooQuery = gql`
query FooQuery($foo: String!) {
getFoo(foo: $foo)
}
`
const barQuery = gql`
query BarQuery($bar: String!) {
getBar(bar: $bar)
}
`
const { document, variables } = combineQuery('FooBarQuery')
.add(fooQuery, { foo: 'some value' })
.add(barQuery, { bar: 'another value' })
console.log(variables)
// { foo: 'some value', bar: 'another value' }
print(document)
/*
query FooBarQuery($foo: String!, $bar: String!) {
getFoo(foo: $foo)
getBar(bar: $bar)
}
*/
3
-
This seemed hopeful, but it's really fresh and has bugs. Not the least of which is the package name being misspelled in package.json.
– ivanjonasJul 16, 2020 at 13:28
-
@ivanjonas, what was the bug, other than package name?
– Domas LapinskasJul 16, 2020 at 17:31
-
@DomasLapinskas Does this limit the network calls as well to just one ?
– ShujathFeb 11, 2021 at 13:17
Now with graphql-request
you can also do batching with queries:
import { batchRequests, gql } from 'graphql-request';
const bookQuery = gql`
query book($title: String!) {
book(title: $title) {
title
}
}
`;
const endpoint = 'localhost/graphql/api/';
const books = await batchRequests(endpoint, [
{ document: bookQuery, variables: { title: 'Book 1' } },
{ document: bookQuery, variables: { title: 'Book 2' } },
]);
Then you’ll have books
as a list of { data: book: { title } }
. So it works with queries, mutation and duplicates.
Nobody mentioned another straightforward variant. It is possible to merge (combine) multiple gql queries into one with interpolation (credit to @maxdarque):
const fieldsOnBook = gql`
fragment fieldsOnBook on Book {
id
author
}
`
const fieldsOnCar = gql`
fragment fieldsOnCar on Car {
id
name
}
`
const bookQuery = gql`
query ($bookId: ID!) {
book(id: $bookId) {
.... fieldsOnBook
}
}
${fieldsOnBook}
`
const carQuery = gql`
query ($carId: ID!) {
car(id: $carId) {
...fieldsOnCar
}
}
${fieldsOnCar}
`
const oneQuery = gql`
query ($bookId: ID! $carId: ID!) {
book(id: $bookId) {
.... fieldsOnBook
}
car(id: $carId) {
... fieldsOnCar
}
}
${fieldsOnBook}
${fieldsOnCar}
`
are you trying to minimize the number of HTTP requests? if yes this might help blog.apollographql.com/query-batching-in-apollo-63acfd859862
Feb 18, 2019 at 9:35
You can't do this through simple textual manipulation; you need to parse the GraphQL queries and join them together. That can get complicated in the presence of dynamic type matching and fragments.
Feb 18, 2019 at 11:56