Hasuras comparison and ordering expressions / arguments / variables in an optimistic offline scenario #1438
Replies: 3 comments
-
I feel like it's what @chrisdrackett wanted to have in this thread #1058 |
Beta Was this translation helpful? Give feedback.
-
This looks like a really cool abstraction to automatically understand Hasura's regularised schema! I suppose the problem I see is that a highly-normalised schema that's only denormalised by relations is a rather rare occurrence, since it won't be an API that necessarily caters for apps, but nonetheless it's exciting that this can be highly automated! On a side note, we're currently exploring APIs that make |
Beta Was this translation helpful? Give feedback.
-
Ah, interesting to hear! Thanks for the feedback. I'm not sure I understand what you mean with "being only denormalized by relations". Do you mean that you think its rare that the only server-side-effects are where/order? Cause I absolutely understand that. As soon as you mix in pagination or custom server stuff you're obviously kind of at a dead end since you can't reliably know on the client where in the whole table an entity was/is or what else the server does. But still, I think it's pretty interesting for when you expect very little results on a query and don't need pagination, that's also the area where a user might expect things to "just work" offline. In the meantime I fully implemented it with my most used 4 basic where-operators (WIP gist) and all order-operators (feature complete), working pretty nicely so far. My update function for updating a single task was gigantic and is now reduced to this with the introduction of the custom # All not done, not deleted tasks for a workspace ordered by custom order and date
query ListOpenTasks($workspace: Int!) {
task(
where: { workspace_id: { _eq: $workspace }, is_done: { _eq: false }, deletion_date: { _is_null: true } }
order_by: { order: asc_nulls_last, updated_at: desc }
) {
...TaskFragment
}
}
# All recently completed tasks, ordered by completion date
query ListRecentlyDoneTasks($workspace: Int!, $_gte: timestamptz = "") {
task(
where: {
workspace_id: { _eq: $workspace }
is_done: { _eq: true }
done_date: { _gte: $_gte }
deletion_date: { _is_null: true }
}
order_by: { done_date: desc }
) {
...TaskFragment
}
}
# On the logbook we only group by done date, but don't show deleted tasks
query LogbookList($workspace: Int!) {
task(
where: { workspace_id: { _eq: $workspace }, is_done: { _eq: true }, deletion_date: { _is_null: true } }
order_by: { done_date: desc }
) {
title
id
is_done
done_date
}
} update_task_by_pk: (result, untypedArgs, cache) => {
const args = untypedArgs as Mutation_RootUpdate_Task_By_PkArgs
if (!args || !args.pk_columns || !args._set) {
throw new Error('Update task mutation is missing vital fields')
}
// retrieve the workspace_id for the vars
const workspaceId = cache.resolve({ __typename: 'task', id: args.pk_columns.id }, 'workspace_id')
// construct an updateQuery function for this mutation
const updateQuery = updateQueryFactory<Task>({
keyedResult: result.update_task_by_pk as Task,
args: untypedArgs,
cache,
typename: 'task'
})
// 1) list open
updateQuery({
query: ListOpenTasksDocument,
variables: { workspace: workspaceId }
})
// 2) recently done tasks
// all queries; note that our hasura query root is called query_root
const doneQueries = cache
.inspectFields('query_root')
// @ts-expect-error urql types are too basic to understand that is_done exists
.filter((x) => x.fieldName === 'task' && x.arguments?.where?.is_done?._eq === true)
// resolve the workspace id for our query updates
doneQueries.forEach((x) => {
const doneQueryArgs = x.arguments as Query_RootTaskArgs
updateQuery({
query: ListRecentlyDoneTasksDocument,
variables: { workspace: workspaceId, _gte: doneQueryArgs.where?.done_date?._gte }
})
})
// 3) Logbook List
updateQuery({
query: LogbookListDocument,
variables: { workspace: workspaceId }
})
} |
Beta Was this translation helpful? Give feedback.
-
If I understood graphcache correctly, it is capable of caching normalized entities, but can't know how the backend resolves entities into queries. Or in other words since graphcache can't know what a
_gte
argument in a query means, we need to update queries within the graphcacheupdates
ourselves to emulate what the backend would do (especially when it comes to ordering).So, again, if understood correctly – if there is "fat" code on the
updates
that emulates the behavior of the backend, we should get completely identically ordered (optimistic) updates, even in an offline scenario without re-fetching.In my case I'm using hasura, which has Comparison Expressions like _eq, _is_null, or _gte and ordering expressions like asc or asc_nulls_first, eg:
I started working on an abstraction where within
updates
for a mutation I supply a new function called updateQuery:and then it reads the GraphQL code generator generated
ListOpenTaskDocument
-definitions liketransforms the where and order arguments like eg. (just some prototyping code)
and manually adds or removes the document(s) from the update mutation like eg.
All these snippets are not cleaned, completely generic or anything and some vars are missing / out of context, but just to illustrate the idea better.
I'm still working things out and am happy to share once finished, but in the meantime I was just curious if I'm on the right track or if there's some shortcut I didn't take into consideration. Or is there any other way to get completely identically ordered (optimistic) updates, even in an offline scenario without re-fetching when using variables/arguments?
Would love to hear your thoughts @kitten @JoviDeCroock
Beta Was this translation helpful? Give feedback.
All reactions