Reputation: 4908
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.
Upvotes: 26
Views: 49203
Reputation: 21757
Nobody mentioned another straightforward variant. It is possible to merge (combine) multiple gql queries into one with interpolation (credit to @maxdarque):
import { gql } from '@apollo/client'
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}
`
Upvotes: 2
Reputation: 7059
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.
Upvotes: 3
Reputation: 640
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
Upvotes: 7
Reputation: 199
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)
}
*/
Upvotes: 7
Reputation: 7666
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
}
}
Upvotes: 14