Query Side Effects
Query Side Effects are a Moopsy concept where a single trip to the backend server performs both a mutation and one or more queries
as a side effect in a single trip. When the response gets back to the client, the UI moopsyly (get it?) updates, as mutation.isLoading
changing to false
and the new data being available from the query happen simultaneously.
The result is a fantastic user experience where as soon as loading completes the updated state is immediately visible.
What exactly happens?
- The client makes a request to a mutation, passing in the queries as a side effect in the message sent to the server
- The server executes the mutation (if an error is thrown, this error is returned to the client and the queries are ignored)
- The server executes the attached queries in parallel
- The server returns in a single message both the relevant mutation and the result of the attached queries
- All-at-once, the result of the mutation is fulfilled in the promise returned from
.call(...)
, and the.data
parameter of all the queries are updated
What about optimistic rendering?
QSEs are not a replacement for optimistic rendering (Moopsy, currently, does not handle optimistic rendering in its scope). The old query state will be shown until the mutation is fully completed on the server.
How can I optimize my endpoints for this?
One of the easiest ways to optimize your endpoints on the backend is to defer any work in your mutation endpoints that are not critical to
the state being updated. For example, if your endpoint is all about sending a chat message, you could await
calls to your DB and cache to insert the
new chat message, but not await requests to send push notifications or non critical db updates (like updating counters).
Why?
- Reduce the number of trips
- Simplify client-side logic (no need to have to call
.refresh()
)
How do I use this?
Crazy simple:
const someQuery = moopsyClient.useQuery<SomeQueryBP.Plug>(SomeQueryBP);
const someMutation = moopsyClient.useMutation<SomeMutationBP.Plug>(SomeMutationBP, { querySideEffects: [someQuery] });
The queries you pass in to querySideEffects
can come from anywhere, including props or even contexts. If you're a fan of
using contexts over state-management like Redux, you can pass down both the state value and the query in your context, and anywhere
you perform a mutation that can cause a side-effect just pass the query from the context into querySideEffects
.
When should I not use this?
Don't use Query Side Effects when they query has a large amount of data to fetch and your mutation only changes
For example, let's say you have a to-do app with hundreds of todos, and your mutation adds a new todo. Running an entire query side effect re-fetching hundreds of todos from your DB or cache and transmitting all down the wire again just because a single todo got inserted will be a major performance drain. Instead, you should probably use PubSub to publish individual updates to the data. You could also utilize Optimistic Rendering, but that is outside the scope of Moopsy.