apollo-ios icon indicating copy to clipboard operation
apollo-ios copied to clipboard

Support query batching

Open danwkennedy opened this issue 7 years ago • 29 comments

We've come across a situation where query (and mutation) batching would be very useful. I noticed that this was an issue in the Android project but not in the iOS one so I wanted to add a reference here.

https://github.com/apollographql/apollo-android/issues/513

danwkennedy avatar Dec 07 '17 15:12 danwkennedy

Thanks! Once https://github.com/apollographql/apollo-ios/pull/186 lands that should make it easier to add support for query batching.

Out of curiosity, what is your use case? Especially with HTTP/2, query batching doesn't seem to have that many performance benefits and may actually degrade performance.

martijnwalraven avatar Dec 07 '17 20:12 martijnwalraven

We'd like to use it to pass n-mutations in a set order. Our specific use case is the removal of n items in a hierarchical list (where you can't remove the parent before the children are removed) in one go. An example for a mutation like this could be:

mutation {
  child1: remove(id: "2"){ id }
  child2: remove(id: "3"){ id }
  child3: remove(id: "4"){ id }
  parent: remove(id: "1"){ id }
}

We didn't want to go the route of having the removal of the parent take the children so we can catch potentially erroneous behavior and ask the user what they want to do (i.e. move the children to another parent, or actually remove all the children).

danwkennedy avatar Dec 07 '17 21:12 danwkennedy

Ah, I see. Query batching as currently implemented in Apollo Server would work for this I think, but ordering isn't actually guaranteed (unlike putting multiple mutations in a single operation, as you've done above).

Would something like remove(id: ..., cascadeToChildren: true) or a batch removeAll(ids: ) be an alternative?

martijnwalraven avatar Dec 07 '17 21:12 martijnwalraven

I was considering the removeAll(ids: [ID]!) route. The only downside is we lose the nice error tracing that GraphQL gives us. i.e. If ID number 3 fails, then the multiple mutations in a single operation would give us a trace to that failed mutation and the client would know to deal with just that failed mutation. With removeAll, a custom error format would be required to give us that kind of functionality.

I also think I might not quite understand how query batching works in Apollo Server. Does the batching simply let you send multiple operations in a row? If that's the case then you're right it's probably not what I'm looking for. Instead what I was describing is a sort of mutateInSequence method that takes a list of mutations and merges them into that un-named operation and runs that. It'd be really cool but also sounds nasty to implement 😄

danwkennedy avatar Dec 08 '17 14:12 danwkennedy

Depending on the underlying service or datastore, a single mutation would have the benefit that you can run it in a transaction and have everything automatically rolled back when a removal fails. But you're right, there is no built-in way to include an error per id. Perhaps you could define a custom result object if you really need it. Something like:

removeAll(ids: $ids) {
  removedItems {
    id
  }
}

Query batching in Apollo Server is indeed just a way to send multiple operations in one request. Currently operations are executed in sequence I believe, but it is not guaranteed and we may want to execute them in parallel.

martijnwalraven avatar Dec 08 '17 14:12 martijnwalraven

Hi @martijnwalraven, are there any plans on adding support for Transport-level query batching? Our app can do multiple queries at once and then it would be useful if those could be send to the server in 1 request instead of multiple request.

nuke-dash avatar Nov 16 '18 11:11 nuke-dash

Hey guys,

Do we have any plans on supporting query batching? Is there a doc we can refer to about the roadmap of Apollo-iOS?

thonydam avatar Apr 18 '19 17:04 thonydam

Marking this as an enhancement - I don't have a timeline for this but it does seem like a useful thing to add support for to reduce the number of round-trips necessary if possible.

@thonydam Right now we don't have a roadmap doc, but I'll be working on that at some point in the next few months once I get the open issues a bit more under control.

designatednerd avatar Jul 11 '19 15:07 designatednerd

@designatednerd Thanks for the update and your work! Glad to see that this repo will get more love!

thonydam avatar Jul 12 '19 05:07 thonydam

Any updates on this topic? (This topic is mixed with another and followed up on https://github.com/apollographql/apollo-ios/issues/884. Not sure what the current status is though).

cezarsignori avatar Jun 02 '20 13:06 cezarsignori

I'm going to look at adding both of these as we update the networking stack but I do not have an ETA.

designatednerd avatar Jun 02 '20 20:06 designatednerd

@designatednerd - can this issue be re-examined for viability following the network stack rearchitecture?

benjaminsaurusrex avatar Apr 13 '21 16:04 benjaminsaurusrex

Has there been any update to enable query batching for iOS? My team is hoping to use batching for web and android requests but we're not sure this is the right path since it isn't currently supported for apollo client in iOS.

@danwkennedy @designatednerd @martijnwalraven

daniel-r-hartman avatar Jan 03 '22 21:01 daniel-r-hartman

Hi @daniel-r-hartman - Our team won't be able to get to this until v2.0 where networking will be the focus of that major version. We are always happy to work with community member contributions though; reviewing and guiding where we can.

calvincestari avatar Jan 04 '22 18:01 calvincestari

Thanks for the reply @calvincestari. Is there any timeline or estimate of when this feature might be available?

My team is currently evaluating working on a custom solution for batching, vs contributing to this project, vs non-batching solutions.

daniel-r-hartman avatar Jan 04 '22 19:01 daniel-r-hartman

There is no estimated timeline for v2.0 yet. We're still working on v1.0 which is focussed on code generation. If I were to guess at a timeline right now I'd say second half of 2022.

calvincestari avatar Jan 04 '22 19:01 calvincestari

Assume this is still pending? Thanks!

tspike avatar May 09 '23 00:05 tspike

Assume this is still pending? Thanks!

We haven't started on 2.0 yet. This will be included in that work.

calvincestari avatar May 09 '23 02:05 calvincestari

I am currently using apollo client 1.2.0 ios version, but query batching is not available though. Could you please update on this

ArunVicky001 avatar Jun 22 '23 13:06 ArunVicky001

Hi @ArunVicky001. Query batching is still not a native feature of Apollo iOS but it is possible and you will need to code your own request body creator to get it working.

0.51.1 included a change to expose the request body creation. This is the hook for you to use and build a custom request body that would batch together the operations. You'll also probably need a custom response interceptor to parse the response if it's going to include more than a single data object in the body.

calvincestari avatar Jun 22 '23 18:06 calvincestari

@calvincestari which means we need to make change in the query itself right?

ArunVicky001 avatar Jun 27 '23 09:06 ArunVicky001

You need to change the request that gets sent to the server. You cannot change the GraphQL query to enable batching.

calvincestari avatar Jun 27 '23 17:06 calvincestari

Got it,

on other hand can we implement like this?

import Apollo

let apolloClient = ApolloClient(url: URL(string: "https://api.graphql.org/")!)

let query1 = """
query {
  viewer {
    name
  }
}
"""

let query2 = """
query {
  repository(owner: "apollographql", name: "apollo-client") {
    name
  }
}
"""

let dispatchGroup = DispatchGroup()

var viewerName: String?
var repositoryName: String?

dispatchGroup.enter()
apolloClient.fetch(query: query1) { result in
  defer { dispatchGroup.leave() }
  
  switch result {
  case .success(let graphQLResult):
    viewerName = graphQLResult.data?.viewer?.name
  case .failure(let error):
    print("Error querying viewer: \(error)")
  }
}

dispatchGroup.enter()
apolloClient.fetch(query: query2) { result in
  defer { dispatchGroup.leave() }
  
  switch result {
  case .success(let graphQLResult):
    repositoryName = graphQLResult.data?.repository?.name
  case .failure(let error):
    print("Error querying repository: \(error)")
  }
}

dispatchGroup.notify(queue: .main) {
  if let name = viewerName {
    print("Viewer Name: \(name)")
  }
  if let repoName = repositoryName {
    print("Repository Name: \(repoName)")
  }
}

ArunVicky001 avatar Jun 28 '23 06:06 ArunVicky001

Yes, you could do that. It's slightly more complicated for the client but the end result of time-to-wait for the user should be the same as query batching on the server because the server wouldn't combine the responses, i.e.: the client would still have to parse two data responses.

The best eventual solution for this is probably to use @defer. We're working on it now for Apollo iOS but it would also require changes to your queries to combine the fields you want immediately and the ones that can be deferred.

calvincestari avatar Jun 28 '23 17:06 calvincestari

@ArunVicky001 I'm going to close this issue now as there isn't anything actionable for our team.

calvincestari avatar Jun 28 '23 17:06 calvincestari

Oh wait, this is a long-standing issue we're keeping around for the 2.0 work. My mistake.

calvincestari avatar Jun 28 '23 17:06 calvincestari

What is the update? Any luck for query batching

ArunVicky001 avatar Jul 03 '23 17:07 ArunVicky001

@ArunVicky001 This work is going to be included in the work for version 2.0 which doesn't currently have a date however you can follow our roadmap which is updated consistently to keep track of upcoming and planned work.

BobaFetters avatar Jul 05 '23 13:07 BobaFetters

@BobaFetters Thanks for your response..

ArunVicky001 avatar Jul 10 '23 07:07 ArunVicky001