You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Angular v17 includes a stable API for Signals. I am wondering if that might have an impact for apollo-angular or not.
So far I would say that because apollo-angular is mostly doing XHR, and that XHR must be cancellable, then we must keep using observable. And if somebody would rather work with non-cancellable signal (which does not sound like a great idea to me), then it is up to them to adapt their consuming code. So there is nothing to be done within the lib.
But I'd like to hear the thoughts of the community and see if I am maybe missing something ?
Reacted by Sondreproplan, Jared Bates, João Dias, Denis Yılmaz, Ban117, Daniel Schuba, Marvin Heilemann, Panagiotis Kapros, Volodymyr Volodin, Antoine Petot and 3 more
Reacted by Eduardo A Fernandez Diaz, Marvin Heilemann, Sondreproplan, Sergej Sintschilin and Mickaël Olaria
Reacted by Matthew Van der Bijl, Rafael Guimaraes Siqueira, friedhelm-youniverse, Eduardo A Fernandez Diaz, Panagiotis Kapros, Sondreproplan and Ivan
I agree that using observables in the library seems like the best plan. I do however have some questions about using apollo-angular together with signals. There are a few things that I'm running into that make it harder to work with than I want it to be, hopefully you have some ideas how to remedy these.
When I want to consume the query results in my component I use the provided toSignal from Angular. I don't want to check for null values everywhere, so I would like to use { requireSync: true } with an initialValue.
I tried using useInitialLoading: true but that isn't sync. That would also give me back data: Data | null which isn't really what I want.
Now I solve it by adding a startWith({data: initialData, loading: true, errors: undefined }) to the pipe. Before that I have a map operator that removes the queryname property from the data field
because having that extra property only gets in the way (especially in the types, because its dynamic). But that's probably there to accommodate the __typename field I guess?
But having to do this for every query is a bit cumbersome. I would love for an initialValue option in (Watch)QueryOptions that would do the work of the startWith operator above.
Now it might seem a bit weird to have initial data when loading is true, but when you create a signal from your query, I would prefer to not have to null check every computed that is derived from that data. eg:
I'm not sure if that's a real use case or if that would be good architecture. But something like a combination of watchQuery and computed/effect could be helpful. This could look something like this.
So the signalQuery would attach to the signal query and every new value to the query would set the variables on the watchQuery and this would respond with GraphQL response.
I have an actual use case such as the one you describe. My component has an input signal, and the query uses that signal value on its variables. I am currently using an effect which calls fetch.
I would like to be able to use the signal on the query variables for a watchQuery, and to also be able to call refetch on the QueryRef.
In our private projects, we use this kind of pattern a lot. It is super convenient to watch variables. So I wouldn't be against re-adding it. But the question is with what kind of API ?
Most often you need to debounce variables, otherwise there is a high chance you send too many XHR in a very short period of time. That sounds more like a job for Rxjs than for signals to me.
Also, this.apollo.signalQuery() means that an XHR will be sent immediately upon calling that function, because signals cannot be asynchronous. In some cases, where you already have the variables ready, it could be fine. But in our projects we very often setup the observable and subscribe later, when we have the needed variables and when other things around are setup. This would not work anymore.
Firing XHR too soon sounds like big pitfall to me. That's why I still cannot see how an integration with signals would improve the DX.
Also I think we would lose the ability to control when we unsubscribe. While unsubscribing when the component is destroyed can be ok in basic cases, there are a lot of cases where things are not that straightforward. For instance a query inside a service would never be unsubscribed to. That sounds like a huge issue.
If I guess your code correctly, that means an XHR is fired every time the variables changes, potentially extremely often, and the previous XHRs are never cancelled. That can quickly end up being a performance issue.
What I think is a more robust solution, and happens to be doable only with Rxjs AFAIK, is the following:
exportclassFooComponent{privatereadonlyapollo=inject(Apollo);publicreadonlyvariables=input.required<UsersVariables>();publicreadonlyresult: Observable<ApolloQueryResult<Users>>=toObservable(this.variables).pipe(debounceTime(20),// Not too oftenswitchMap(// Cancel previous XHRvariables=>this.apollo.watchQuery<Users,UsersVariables>({query: usersQuery,variables: variables,}).valueChanges,),);}
I have been trying to reproduce something similar where this.result would be a Signal, but I actually couldn't. Even if giving up the debounce and switchMap, I am not sure how to do it... Can you convert my sample into something that looks more like your code ?
tld;dr, I still thinks that signals are not the right tool for handling XHR, because the timing of XHR subscription, cancelling and unsubscription (from watching cache) are critical. And the whole reason for signal to exist is to be purely synchronous, and thus making it impossible to control the timing. Unless by doing hacks to re-expose things, but then you'd be re-implementing Rxjs while fighting signal nature.
Does it cancel XHR though? Looking at angular http client they implemented an abort controller when unsubscribing from the source observable. In query we call the Apollo client with a from promise.
Probably the new resource function from Angular (experimental) could be a great solution to support Angular Signals in apollo-angular => https://angular.dev/guide/signals/resource.
Reacted by Joep van Liempd, Sergej Sintschilin, Roy Peled, seyfert, Iakimchuk Dmitrii, Steven Chim, Emily Klassen, Rene Cincura, Valencio Hoffman, Emily Marigold Klassen and 4 more
Reacted by Rafael Guimaraes Siqueira, Jakub Vančo, Sondreproplan, Jesse Zomer, Emily Klassen, Valencio Hoffman and Paul Reichelt-Ritter
I have been following the Resource RFC closely. And I am happy to see that other people also mentions things that would make it hard/awkward to adopt resource for apollo-angular.
First rxResource can now emit multiple values over time. That was previously a deal breaker. The fact that resource are eager is also problematic, though we can workaround by returning undefined from request. The need to be in injection context (or pass it around) is also not necessarily super ergonomic for us. Finally, the lack of choice between switchMap, concatMap or exhaustMap behavior makes it ill-fitted for mutations. So that's another things that would need some thoughts...
But all of those things have been mentioned multiple times in the RFC, so we'll see what the Angular will do with those feedback.
All of this to say, out of the box resources in apollo-angular might or might not come, but it's definitely not coming anytime soon, because things are not mature enough to rely on it heavily. We'll see how things evolve...
Reacted by Jesse Zomer, João Dias, ivibe, Emily Klassen, Daniel Schuba, Antoine Petot, Vojtech Mašek and Emirhan Yumak
@the-ult what would be your goal for signal-based resources ? What kind of API/usages would you imagine ? For queries ? And for mutations ? And what about usage from services (without natural injection context) ?
I'm already using graphql with the new resource API. I just wrote a small wrapper around a regular post request. It looks good, and could even completely replace Apollo because it provides caching and request status. I'm still experimenting with it; I'm not completely sure of success, but it's a promising start.
Interesting feedback. Apollo provides (much?) more than simple XHR though. The first thing that comes to mind is the normalized cache that notify you when the data you queried has been modified via a mutation from somewhere else in your app. That is incredibly valuable in my opinion.
Then there's the fetchPolicy that allows you to decide whether stale data are acceptable. And probably a lot of other things that you might or might not need too...
But I am curious how you handle mutations, since the Angular team explicitly said resource are not meant for mutations, but only queries. Do you have some code sample you could share ?
Interesting feedback. Apollo provides (much?) more than simple XHR though.
I was obviously incorrect when I said I could "completely" replace it. What I really meant was that it was sufficient in my case. Apollo certainly does a lot more work to be replaced with just a few lines of code. Also, resources aren't designed for mutation; so far, I've only used them for data retrieval. As I said earlier, I'm still experimenting with this approach, and if it turns out badly, I'll be sure to report it in this thread.
Activity
JesseZomer commentedon Dec 8, 2023
Hello,
I agree that using observables in the library seems like the best plan. I do however have some questions about using apollo-angular together with signals. There are a few things that I'm running into that make it harder to work with than I want it to be, hopefully you have some ideas how to remedy these.
When I want to consume the query results in my component I use the provided
toSignalfrom Angular. I don't want to check for null values everywhere, so I would like to use{ requireSync: true }with an initialValue.I tried using
useInitialLoading: truebut that isn't sync. That would also give me backdata: Data | nullwhich isn't really what I want.Now I solve it by adding a
startWith({data: initialData, loading: true, errors: undefined })to the pipe. Before that I have a map operator that removes the queryname property from the data fieldbecause having that extra property only gets in the way (especially in the types, because its dynamic). But that's probably there to accommodate the __typename field I guess?
But having to do this for every query is a bit cumbersome. I would love for an
initialValueoption in (Watch)QueryOptions that would do the work of the startWith operator above.Now it might seem a bit weird to have initial data when loading is true, but when you create a signal from your query, I would prefer to not have to null check every
computedthat is derived from that data. eg:instead of
Got any tips op workarounds for how best deal with these situations?
PowerKiKi commentedon Dec 11, 2023
For your use-case I think it would be possible to write your own function that wraps everything you need. Probably something similar to:
It's only pseudo-code, but it should be possible to make it work.
For the time being I'd probably avoid introducing signal-related things in this package, unless we have a clear and strong need for it.
DaSchTour commentedon Apr 25, 2024
I'm not sure if that's a real use case or if that would be good architecture. But something like a combination of watchQuery and computed/effect could be helpful. This could look something like this.
So the signalQuery would attach to the signal
queryand every new value to the query would set the variables on the watchQuery and this would respond with GraphQL response.rafagsiqueira commentedon Apr 25, 2024
I have an actual use case such as the one you describe. My component has an input signal, and the query uses that signal value on its variables. I am currently using an effect which calls fetch.
I would like to be able to use the signal on the query variables for a watchQuery, and to also be able to call refetch on the QueryRef.
PowerKiKi commentedon Apr 26, 2024
@DaSchTour,
I assume you meant "every new value to the variables" ? Because your goal is to react when variables change ? So your prototype would be more like:
This kind of thing, watching variables, does not exist anymore in
apollo-angular. It was removed before v1 landed. And the removal caused some trouble back then.In our private projects, we use this kind of pattern a lot. It is super convenient to watch variables. So I wouldn't be against re-adding it. But the question is with what kind of API ?
Most often you need to debounce variables, otherwise there is a high chance you send too many XHR in a very short period of time. That sounds more like a job for Rxjs than for signals to me.
Also,
this.apollo.signalQuery()means that an XHR will be sent immediately upon calling that function, because signals cannot be asynchronous. In some cases, where you already have the variables ready, it could be fine. But in our projects we very often setup the observable and subscribe later, when we have the needed variables and when other things around are setup. This would not work anymore.Firing XHR too soon sounds like big pitfall to me. That's why I still cannot see how an integration with signals would improve the DX.
Also I think we would lose the ability to control when we unsubscribe. While unsubscribing when the component is destroyed can be ok in basic cases, there are a lot of cases where things are not that straightforward. For instance a query inside a service would never be unsubscribed to. That sounds like a huge issue.
@rafagsiqueira,
If I guess your code correctly, that means an XHR is fired every time the variables changes, potentially extremely often, and the previous XHRs are never cancelled. That can quickly end up being a performance issue.
What I think is a more robust solution, and happens to be doable only with Rxjs AFAIK, is the following:
I have been trying to reproduce something similar where
this.resultwould be a Signal, but I actually couldn't. Even if giving up the debounce and switchMap, I am not sure how to do it... Can you convert my sample into something that looks more like your code ?tld;dr, I still thinks that signals are not the right tool for handling XHR, because the timing of XHR subscription, cancelling and unsubscription (from watching cache) are critical. And the whole reason for signal to exist is to be purely synchronous, and thus making it impossible to control the timing. Unless by doing hacks to re-expose things, but then you'd be re-implementing Rxjs while fighting signal nature.
ChandlerBaskins commentedon May 7, 2024
Does it cancel XHR though? Looking at angular http client they implemented an abort controller when unsubscribing from the source observable. In query we call the Apollo client with a from promise.
PowerKiKi commentedon May 8, 2024
Yes, apollo-angular will cancel XHR if you use our
HttpLink, or ourHttpBatchLink. And apollo-client will reject their promise if our link is erroring.cwaespi commentedon Nov 25, 2024
Probably the new
resourcefunction from Angular (experimental) could be a great solution to support Angular Signals in apollo-angular => https://angular.dev/guide/signals/resource.Kind of like this:
nektobit commentedon Feb 28, 2025
@cwaespi
To me it looks great. As far as I understand, all that remains is to wait for the response from the Apollo team, will it go into production?
Celtian commentedon Mar 25, 2025
FYI There is also
rxResource... (also experimental)PowerKiKi commentedon Mar 25, 2025
I have been following the Resource RFC closely. And I am happy to see that other people also mentions things that would make it hard/awkward to adopt resource for apollo-angular.
First
rxResourcecan now emit multiple values over time. That was previously a deal breaker. The fact that resource are eager is also problematic, though we can workaround by returningundefinedfromrequest. The need to be in injection context (or pass it around) is also not necessarily super ergonomic for us. Finally, the lack of choice betweenswitchMap,concatMaporexhaustMapbehavior makes it ill-fitted for mutations. So that's another things that would need some thoughts...But all of those things have been mentioned multiple times in the RFC, so we'll see what the Angular will do with those feedback.
All of this to say, out of the box resources in apollo-angular might or might not come, but it's definitely not coming anytime soon, because things are not mature enough to rely on it heavily. We'll see how things evolve...
the-ult commentedon Nov 3, 2025
With (rx)Resource becoming more stable in v21. Is there a change/possibility to look into this again?
PowerKiKi commentedon Nov 3, 2025
@the-ult what would be your goal for signal-based resources ? What kind of API/usages would you imagine ? For queries ? And for mutations ? And what about usage from services (without natural injection context) ?
nektobit commentedon Nov 19, 2025
I'm already using graphql with the new resource API. I just wrote a small wrapper around a regular post request. It looks good, and could even completely replace Apollo because it provides caching and request status. I'm still experimenting with it; I'm not completely sure of success, but it's a promising start.
PowerKiKi commentedon Nov 19, 2025
Interesting feedback. Apollo provides (much?) more than simple XHR though. The first thing that comes to mind is the normalized cache that notify you when the data you queried has been modified via a mutation from somewhere else in your app. That is incredibly valuable in my opinion.
Then there's the fetchPolicy that allows you to decide whether stale data are acceptable. And probably a lot of other things that you might or might not need too...
But I am curious how you handle mutations, since the Angular team explicitly said resource are not meant for mutations, but only queries. Do you have some code sample you could share ?
nektobit commentedon Nov 26, 2025
I was obviously incorrect when I said I could "completely" replace it. What I really meant was that it was sufficient in my case. Apollo certainly does a lot more work to be replaced with just a few lines of code. Also, resources aren't designed for mutation; so far, I've only used them for data retrieval. As I said earlier, I'm still experimenting with this approach, and if it turns out badly, I'll be sure to report it in this thread.