Apollo GraphQL: Delay Query Refetch - graphql

Senario:
Table of users populated from graphQL query that runs an AWS OpenSearch (e.g. ElasticSearch) on the server.
After successfully adding a user using (using a graphQL mutation), want to refetch the user table.
The issue is OpenSearch queries are batched by the server. This means there is a delay between when the database is updated and when the update results are available to openSearch.
Question: Is there a way to delay the refetch on a query (not the mutation).
Hacky fix (it works, but hate it) in calling component:
const { users, usersLoading, refetch } = useGetUsers() // graphQL query
const onAddUserSuccess = () => {
// executed after the user successfully added
timeOut(() => { refetch() }, [1000])
}
Also, tried in the api queries and mutations (didn't work because of the server batching delay):
// fetch Query (would like to dealy the refetch or check if data has updated before refetching)
const { refetch, loading, error, data } useQuery(
query,
{ variables: ... },
fetchPolcy:
'network-only',
options: { awaitRefetchQueries: true }
)`
// Add User mutation
const refetchUsers = gql`...query`
const [addUser, { loading, error, data }] = useMutation(
mutation,
{
refetchQueries: () => {
return [
{
query: refetchUsers,
variables: {
...
}
}
];
}
}
);

Related

graphql-codegen + typescript-svelte-apollo - Refetch not working?

(Edited)
Refetching with the generated query type seems to work different from the refetch function in Apollo Client useQuery. I don't understand how to phrase it - can anyone provide an example?
I'm realizing the problem is probably either my refetch is not properly phrased, or maybe the store is only hitting the cached query. I've been going over my code for days and I can't figure out what it could be. I've tried await blocks too.
The refetch worked with svelte-apollo, but i'm trying to eliminate that dependency. I've also tried Apollo Client's useQuery, but the whole point of graphql-codegen with typescript-svelte-apollo is to use the generated typescript wrapper for the query.
When I assign the generated query to a reactive constant in my Svelte front-end code,
$: observations = getObservations({ variables: { filter } });
the query does not refetch when i update the query variables, as I would expect.
This is how my svelte template is using the query. The filter object changes based on a form user input. I've tried this with an await block too.
<script lang="ts">
import { getObservations } from '$lib/generated';
$: observations = getObservations({ variables: { filter } });
function handleFilter(event) {
filter = event.detail;
}
</script>
{#if $observations.loading}
Loading...
{:else if $observations.error}
{$observations.error}
{:else if $observations.data}
{#each $observations.data['observations']['edges'] as edge}
<Item node={edge['node']} />
{/each}
{/if}
Since this plugin allows to use the query directly, without Apollo's useQuery, i'm not sure how to phrase a refetch.
If i do $observations.refetch(); inside handleFilter(e), i get an error
Property 'refetch' does not exist on type 'Readable<ApolloQueryResult<GetObservationsQuery> & { query: ObservableQuery<GetObservationsQuery, Exact<{ filter?: FilterObservationsInput; }>>; }>'.ts(2339)
There's nothing fancy in my config. Am I doing something wrong here?
schema: src/graphql/schema.graphql
documents:
- src/graphql/queries.graphql
- src/graphql/mutations.graphql
generates:
src/lib/generated.ts:
plugins:
- typescript
- typescript-operations
- graphql-codegen-svelte-apollo
config:
clientPath: src/lib/shared/client
# asyncQuery: true
scalars:
ISO8601Date: Date
ISO8601DateTime: Date
Here's the client:
export default new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
observations: relayStylePagination(),
},
},
},
})
});
The generated query:
export const getObservations = (
options: Omit<
WatchQueryOptions<GetObservationsQueryVariables>,
"query"
>
): Readable<
ApolloQueryResult<GetObservationsQuery> & {
query: ObservableQuery<
GetObservationsQuery,
GetObservationsQueryVariables
>;
}
> => {
const q = client.watchQuery({
query: GetObservationsDoc,
...options,
});
var result = readable<
ApolloQueryResult<GetObservationsQuery> & {
query: ObservableQuery<
GetObservationsQuery,
GetObservationsQueryVariables
>;
}
>(
{ data: {} as any, loading: true, error: undefined, networkStatus: 1, query: q },
(set) => {
q.subscribe((v: any) => {
set({ ...v, query: q });
});
}
);
return result;
}
Here's the query document that it's built from:
query getObservations($filter: FilterObservationsInput) {
observations(filter: $filter) {
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
createdAt
updatedAt
when
where
imgSrcThumb
imgSrcSm
imgSrcMed
thumbImage {
width
height
}
name {
formatName
author
}
user {
name
login
}
rssLog {
detail
}
}
}
}
}

Apollo Client refetch() is not updating cache when the response from server is empty list

I am using refetch() whenever the variables of the useQuery changes in order to filter the list. Every time a variable prop is changed it fetches the results according to the filters, when the results from the server are empty list [], the refetch() method returns the cached results and not the empty list [].
const { loading, error, data, fetchMore, refetch } = useQuery(Users, {
variables: {
userNumber: userNum,
},
});
useEffect(() => {
if (userNum){
refetch();
}
}, [userNum]);
So it fetches the updated result every time userNum prop changes with the list of users that have this userNum however, if the list of users returned from the server is empty (as no user have this number), I can see the previous cached results instead of an empty list.
I have tried
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',
and still get the cached results instead of the empty list.
I have also tried the merge() in typePolicies and it worked! However when I apply the relayStylePagination it fetches again the cached results and not the empty list.
uri: 'http://localhost:5000/graphql',
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
devices: relayStylePagination(),
merge(existing = {}, incoming) {
return { ...existing, ...incoming };
},
},
},
},
}),
});
I have also tried with keyfields and again is working the cached are updated when the server is returning empty list, however the relay pagination is not working. The only way to have the relay pagination work is to place it only like this:
fields: {
devices: relayStylePagination(),
},
`
My query is like this:
$after: ID
$userNum: Int
$dpiGt: String
$dpiLt: String
) {
users(
after: $after
userNumber: $userNum
first: 30
filter: { dpiGt: $dpiGt, dpiLt: $dpiLt }
) {
edges {
cursor
node {
id
name
userNumber
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
System details:
OS: macOS 10.15.4
Binaries:
Node: 12.16.1 - ~/n/bin/node
Yarn: 1.21.1 - /usr/local/bin/yarn
npm: 6.13.4 - ~/n/bin/npm
Browsers:
Chrome: 86.0.4240.80
Firefox: 81.0.1
Safari: 13.1
npmPackages:
#apollo/client: ^3.2.3 => 3.2.5
Not sure this will fix your issue but it's worth a try.
After investigating and reading the Apollo documentation, under the refetch section, I found this description which might be useful to your use case.
Luckily, the useQuery hook's result object provides fine-grained information about the status of the query via the networkStatus property. To take advantage of this information, we need to set the notifyOnNetworkStatusChange option to true so our query component re-renders while a refetch is in flight:
import { useQuery, NetworkStatus } from '#apollo/client';
...
const { loading, error, data, fetchMore, refetch, networkStatus } = useQuery(Users, {
variables: {
userNumber: userNum,
},
notifyOnNetworkStatusChange: true,
});
if (networkStatus === NetworkStatus.refetch) return <div>Refetching!</div>;
...

using a single query to call multiple queries using apollo react hooks

I am new to this graphql, so what i am trying to achieve here is, i need to call two queries parallelly basically combining the queries and get the loading at a single time.
here i have seperated the queries and using two loading state, i need to combine it like compose.
The use case is, user clicks on the button on the button click i will pass the id, and hit both queries and get the list of data
<Button
text="Details"
onClick={() => {
getNames({ variables: { location_id: Number(location_id) } });
getSchools({ variables: { location_id: Number(location_id) } });
}}
>
const [getNames, { loading: namesLoading, data: namesData }] = useLazyQuery(
GET_NAMES
);
const [
getSchools,
{ loading: schoolsLoading, data: schoolsData },
] = useLazyQuery(GET_SCHOOLS);
const GET_NAMES = gql`
query get_names($location_id: Int!) {
get_names(location_id: $location_id) {
id
name
}
}
`;
const GET_SCHOOLS = gql`
query get_schools($location_id: Int!) {
get_schools(location_id: $location_id) {
schools {
id
name
}
}
}
`;
With splitting i am able to see the data, i am using "#apollo/react-hooks" to get the data with useLazyQuery, how can i achieve with this a single query. So instead of multiple loading , error data i can have a single one
Didn't find any compose hook from this library

Apollo cache fails when query variables change

I'm using useLazyQuery() to get analytic data measured by a time filter. When the time filter change, the cache returns the same values despite the filter.
I'm using the default fetchPolicy (cache-first).
const [
getVisitorsAnalytics,
{ loading, data },
] = useAnalyticVisitorsLazyQuery()
const handleTimeFilterChange = (timeFilter: string) => {
getVisitorsAnalytics({
variables: {
input: {
timeFilter,
},
},
})
}
Thanks and regards!
I think the values are cached and the cache doesn't have the data for the new filter value passed -
const { data, loading } = useLazyQuery(QUERY, {
fetchPolicy: 'network-only'
});

How to update apollo cache after mutation (query with filter)

I am pretty new to GraphQL. I am using graph.cool in a Vue.js project with apollo.
I am using right now the in-memory cache.
I had previously a simple 'allPosts' query.
And after creating a new one, I used the update() hook and readQuery() + writeQuery()
However I want that logged in users can only see their posts. So I modified the query with a filter.
query userStreams ($ownerId: ID!) {
allStreams(filter: {
owner: {
id: $ownerId
}
}) {
id
name
url
progress
duration
watched
owner {
id
}
}
}
My thought was, that I only need to pass in the userid variable. However this is not working. I am always getting
Error: Can't find field allStreams({"filter":{"owner":{}}}) on object (ROOT_QUERY) undefined.
this.$apollo.mutate({
mutation: CREATE_STREAM,
variables: {
name,
url,
ownerId
},
update: (store, { data: { createStream } }) => {
const data = store.readQuery({
query: USERSTREAMS,
variables: {
id: ownerId
}
})
data.allStreams.push(createStream)
store.writeQuery({
query: USER_STREAMS,
variables: {
id: ownerId
},
data
})
}
})
When you use readQuery or writeQuery, you should use the same variable name. So replace
variables: { id: ownerId }
With
variables: { ownerId }
Also, the reason you are getting an exception is that readQuery throws an exception if the data is not in the store. That happens before the first time you use writeQuery (or get the data with some other query).
You could write some default values to the store before calling this mutation.
You could also use readFragment that returns null instead of throwing an exception. But that would require more changes to your code.

Resources