Hendrik Jan
Hendrik Jan

Reputation: 4908

Graphql merge (combine) multiple queries into one?

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

Answers (5)

Roman
Roman

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

Sylhare
Sylhare

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

Kevin K.
Kevin K.

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

Domas Lapinskas
Domas Lapinskas

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

Herku
Herku

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

Related Questions