apollographql / apollo-ios

📱  A strongly-typed, caching GraphQL client for iOS, written in Swift.
https://www.apollographql.com/docs/ios/
MIT License
3.88k stars 728 forks source link

Feature: `@defer` support #2395

Open jpvajda opened 2 years ago

jpvajda commented 2 years ago

One of the disadvantages of GraphQL’s request/response model is that the GraphQL response is not returned to clients until the entire request has finished processing. However, not all requested data may be of equal importance, and in some use cases it may be possible for applications to act on a subset of the requested data. An application can speed up its time-to-interactive if the GraphQL server can send the most important data as soon as it’s ready. The new @defer and @stream directives allow GraphQL servers to do exactly that by returning multiple payloads from a single GraphQL response.

Similar to @stream the @defer directive also allows the client to receive data before the entire result is ready. @stream can be used on list fields. Whereas @defer can be used on used on fragment spreads and inline fragments.

References

https://graphql.org/blog/2020-12-08-improving-latency-with-defer-and-stream-directives/

Iron-Ham commented 1 year ago

I spoke with @calvincestari around getting @defer support in apollo-ios.

There's an ongoing stream of work related to supporting multipart/mixed HTTP requests. To support any kind of incremental transport over HTTP[^1], this work must be completed first.

Once this work has merged, supporting @defer can come in a variety of flavors. I expect that @defer will have the following impacts:

  1. Impact on code generation. Code generation must be impacted when a selection set is deferred. There are questions about how best to do that. The simplest is perhaps to wrap the selection set in an additional layer of Optional, although I fear that this may not convey what's happening to the end user. For example, if I have linked a Twitter account to my GitHub profile, and I @defer retrieval of that information, the generated property would read something like var twitterProfileUrl: String?? { ... }. That's a bit funky, and perhaps doesn't convey that this field will arrive as a String? based on whether the user does or does not have a Twitter profile linked to their GitHub account. Instead, I propose that we explore the possibility of something like this:

    enum ApolloDeferredResponse<T> {
    case loading
    case result(Result<T, Error>)
    }

    Such that this selection set is now wrapped within this property. I think this may well have some effects down the chain (e.g., is SomeFragment? comparable to ApolloDeferredResponse<SomeFragment>?), but it's certainly an idea worth exploring. I'd say that it's certainly a nice to have that could follow an initial proof of concept implementation with optionals.

  2. Impact on inclusion directives: We should certainly think about and test the ergonomics of using the generated code if it's been generated with @skip and @include directives.

  3. Impact on watchers: My assumption for how defer would work is that the incremental responses would pull the record from the cache (in-memory, SQLite, what have you), update/append it with new data, and save it back to the cache. That will certainly trigger a watcher update. Additionally, for a first implementation, we should return incremental callbacks (including the full record as received from previous parts). For folks using the watcher, this will mean getting the data multiple times. This seems likely fine for a first pass at @defer and can be improved upon in future iterations.

Once the support for multipart/mixed is in place, I'd be happy to begin exploring this in collaboration with y'all.

[^1]: Incremental Transport over HTTP

calvincestari commented 1 year ago

Great summary, thanks @Iron-Ham!

john-twigg-ck commented 1 year ago

@calvincestari @Iron-Ham Is there a working branch showing the progress? Cheers for all the work.

calvincestari commented 1 year ago

Nope, not yet.

calvincestari commented 1 year ago

For some more context: version 1.2 has taken my focus over the past few weeks and before that another feature that laid some of the groundwork for the incremental delivery protocol. Now with 1.2 released yesterday I can switch back to focus on @defer. The target date for it was mid-May, it is now early to mid-June.

epitaphmike commented 1 year ago

Does the team have an update on @defer support now that it is GA? Looking forward to being able to test in our iOS and Android apps. Thank you!

calvincestari commented 1 year ago

Does the team have an update on @defer support now that it is GA?

@defer support in Apollo iOS is not GA yet. I'm drafting an RFC and we'll begin the implementation very soon with a release of it by end of July.

epitaphmike commented 1 year ago

Does the team have an update on @defer support now that it is GA?

@defer support in Apollo iOS is not GA yet. I'm drafting an RFC and we'll begin the implementation very soon with a release of it by end of July.

Gotcha. I was referring to @defer being in GA on ApolloClient, but I guess in the case of the docs that is only for React and not iOS.

Is there anything in the works for Kotlin?

When there is an iOS preview we’d be interested in testing.

Thank you

calvincestari commented 1 year ago

Is there anything in the works for Kotlin?

I believe it's already available for Kotlin, it's just the iOS implementation that is lagging.

When there is an iOS preview we’d be interested in testing

👍🏻

calvincestari commented 1 year ago

This is a late update but the RFC is up for review in #3093, and I've started implementation in the feature/defer branch working through the task list in #3093 and creating more as I go.

fabiengasser commented 10 months ago

Any update on this for iOS?

calvincestari commented 10 months ago

Any update on this for iOS?

The first preview release was issued a couple weeks ago - https://github.com/apollographql/apollo-ios/releases/tag/preview-defer.1. We're still finishing it off.

loganblevins commented 3 months ago

How do I disable defer experimental support? I'm on 1.14.0 and the json schema validation is breaking due to defer

loganblevins commented 3 months ago
Error: JavaScriptError: GraphQLSchemaValidationError-There can be only one directive named "@defer".-GraphQLSchemaValidationError@
calvincestari commented 3 months ago

@loganblevins - take a look at your schema, how many defer directive definitions are there? If there is more than one it's correctly a validation error.

loganblevins commented 3 months ago
Screenshot 2024-07-23 at 14 05 29

After running code gen plugin it added this

loganblevins commented 3 months ago

Our schema line 1 has directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT

Why would code gen be creating the duplicate?

loganblevins commented 3 months ago

🐛 link https://github.com/apollographql/apollo-ios/issues/3417

calvincestari commented 3 months ago

@loganblevins - I'll take a look at that issue, thanks.

calvincestari commented 3 months ago

For context on this issue, I'm leaving it open until the Selection Set Initializer work is complete then we can consider @defer support done.

loganblevins commented 3 months ago

@calvincestari Thanks for quick reply! Is there a way I can disable this experimental feature in meantime? I couldn't find that key for my config json