Dependent Queries
You will learn
- How to connect one Query to another
- How to check that dependent Query is stale
- How to add multiple children
- How to add multiple parents
Connections between Queries
Dependent (or serial) Queries depend on previous ones to finish before they can execute. To achieve this, it's as easy as using the connectQuery
method.
Let's create two Queries, the second one will require data from the first one:
// Our Query from the previous section
const characterQuery = createQuery({
handler: async ({ id }) => {
const response = await fetch(`https://rickandmortyapi.com/api/character/${id}`);
return response.json();
},
});
// Let's create a Query that extracts data about character's origin,
// originUrl is a part of response of characterQuery
const originQuery = createQuery({
handler: async ({ originUrl }) => {
const response = await fetch(originUrl);
return response.json();
},
});
Now, we have to connect these Queries to establish static relationship between them.
import { connectQuery } from '@farfetched/core';
connectQuery({
source: characterQuery,
fn({ result: character }) {
return { params: { originUrl: character.origin.url } };
},
target: originQuery,
});
It's done, after every successful execution of characterQuery
, originQuery
will be executed with the parameters returned by fn
in connectQuery
.
Stale state
After established connection, it becomes clear that originQuery
results are useless right after new characterQuery
start. But .$status
of the child Query won't change at parent start because loading of the child query is not started yet.
To determine that data in the Query is outdated (e.g. because parent execution is already started), you can use boolean Store .$stale
.
The typical flow of statuses changes with two dependent queries
Start of the application
- Parent
characterQuery.$status
is "initial"characterQuery.$stale
is false
- Child
originQuery.$status
is "initial"originQuery.$stale
is false
- Parent
Call
characterQuery.start({ id: 1 })
- Parent
characterQuery.$status
is "pending" 🚨characterQuery.$stale
is false
- Child
originQuery.$status
is "initial"originQuery.$stale
is true 🚨
- Parent
characterQuery
successfully finished execution,originQuery
is immediately started with the parameters returned byfn
inconnectQuery
- Parent
characterQuery.$status
is "success"characterQuery.$stale
is false
- Child
originQuery.$status
is "pending" 🚨originQuery.$stale
is true 🚨
- Parent
originQuery
successfully finished execution- Parent
characterQuery.$status
is "success"characterQuery.$stale
is false
- Child
originQuery.$status
is "success"originQuery.$stale
is false
- Parent
Multiple children
connectQuery
accepts an array of children as well. E.g., if we have two Queries that depend on the same data, we can connect them to parent in one call:
connectQuery({
source: characterQuery,
fn({ result: character }) {
return { params: { originUrl: character.origin.url } };
},
target: [originQuery, originDetailsQuery],
});
INFO
All children Queries have to have the same parameters in this form.
Multiple parents
connectQuery
accepts an object with any amount of named parents as well.
connectQuery({
source: { character: characterQuery, language: languageQuery },
fn({ character, language }) {
return {
params: {
originUrl: character.result.origin.url,
language: language.result,
},
};
},
target: originQuery,
});
Behavior of this form is pretty simple — children Queries will be started with the parameters returned by fn
in connectQuery
right after all parents are successfully finished. After first execution, re-execution of any parent will trigger re-execution of all children.
Simplified form
Sometimes, children Queries don't need any parameters. In this case, you can use simplified form of connectQuery
:
connectQuery({
source: characterQuery,
target: originQuery,
});