Skip to content

Commit 6bafc02

Browse files
authored
refactor(types): automatic type inference and strict types (#41)
1 parent 9f26b5e commit 6bafc02

File tree

5 files changed

+234
-59
lines changed

5 files changed

+234
-59
lines changed

app/pages/optimistic-todos.vue

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import { todosQuery } from '~/queries/todos'
3+
24
definePageMeta({
35
middleware: 'auth'
46
})
@@ -8,14 +10,7 @@ const toast = useToast()
810
const { user } = useUserSession()
911
const queryCache = useQueryCache()
1012
11-
const { data: todos } = useQuery({
12-
key: ['todos'],
13-
// using $fetch directly doesn't avoid the round trip to the server
14-
// when doing SSR
15-
// https://github.com/nuxt/nuxt/issues/24813
16-
// NOTE: the cast sometimes avoids an "Excessive depth check" TS error
17-
query: ({ signal }) => useRequestFetch()('/api/todos', { signal }) as Promise<Todo[]>
18-
})
13+
const { data: todos } = useQuery(todosQuery)
1914
2015
const { mutate: addTodo } = useMutation({
2116
mutation: (title: string) => {
@@ -33,7 +28,7 @@ const { mutate: addTodo } = useMutation({
3328
onMutate(title) {
3429
// let the user enter new todos right away!
3530
newTodo.value = ''
36-
const oldTodos = queryCache.getQueryData<Todo[]>(['todos']) || []
31+
const oldTodos = queryCache.getQueryData(todosQuery.key) || []
3732
const newTodoItem = {
3833
title,
3934
completed: 0,
@@ -45,13 +40,10 @@ const { mutate: addTodo } = useMutation({
4540
// we use newTodos to check for the cache consistency
4641
// a better way would be to save the entry time
4742
// const when = queryCache.getEntries({ key: ['todos'], exact: true }).at(0)?.when
48-
const newTodos = [
49-
...oldTodos,
50-
newTodoItem
51-
]
52-
queryCache.setQueryData(['todos'], newTodos)
43+
const newTodos = [...oldTodos, newTodoItem]
44+
queryCache.setQueryData(todosQuery.key, newTodos)
5345
54-
queryCache.cancelQueries({ key: ['todos'], exact: true })
46+
queryCache.cancelQueries({ key: todosQuery.key, exact: true })
5547
5648
return { oldTodos, newTodos, newTodoItem }
5749
},
@@ -60,24 +52,30 @@ const { mutate: addTodo } = useMutation({
6052
// update the todo with the information from the server
6153
// since we are invalidating queries, this allows us to progressively
6254
// update the todo list even if the user is adding a lot very quickly
63-
const todoList = queryCache.getQueryData<Todo[]>(['todos']) || []
55+
const todoList = queryCache.getQueryData(todosQuery.key) || []
6456
const todoIndex = todoList.findIndex(t => t.id === newTodoItem.id)
6557
if (todoIndex >= 0) {
66-
queryCache.setQueryData(['todos'], todoList.toSpliced(todoIndex, 1, todo))
58+
queryCache.setQueryData(
59+
todosQuery.key,
60+
todoList.toSpliced(todoIndex, 1, todo)
61+
)
6762
}
6863
},
6964
7065
onSettled() {
7166
// always refetch the todos after a mutation
72-
queryCache.invalidateQueries({ key: ['todos'] })
67+
queryCache.invalidateQueries({ key: todosQuery.key })
7368
},
7469
7570
onError(err, _title, { oldTodos, newTodos }) {
7671
// oldTodos can be undefined if onMutate errors
7772
// we also want to check if the oldTodos are still in the cache
7873
// because the cache could have been updated by another query
79-
if (newTodos != null && newTodos === queryCache.getQueryData(['todos'])) {
80-
queryCache.setQueryData(['todos'], oldTodos)
74+
if (
75+
newTodos != null
76+
&& newTodos === queryCache.getQueryData(todosQuery.key)
77+
) {
78+
queryCache.setQueryData(todosQuery.key, oldTodos)
8179
}
8280
8381
if (isNuxtZodError(err)) {
@@ -104,31 +102,34 @@ const { mutate: toggleTodo } = useMutation({
104102
}),
105103
106104
onMutate(todo) {
107-
const oldTodos = queryCache.getQueryData<Todo[]>(['todos']) || []
105+
const oldTodos = queryCache.getQueryData(todosQuery.key) || []
108106
const todoIndex = oldTodos.findIndex(t => t.id === todo.id)
109107
let newTodos = oldTodos
110108
if (todoIndex >= 0) {
111109
newTodos = oldTodos.toSpliced(todoIndex, 1, {
112110
...todo,
113111
completed: Number(!todo.completed)
114112
})
115-
queryCache.setQueryData(['todos'], newTodos)
113+
queryCache.setQueryData(todosQuery.key, newTodos)
116114
}
117115
118-
queryCache.cancelQueries({ key: ['todos'], exact: true })
116+
queryCache.cancelQueries({ key: todosQuery.key, exact: true })
119117
120118
return { oldTodos, newTodos }
121119
},
122120
123121
onSettled() {
124122
// always refetch the todos after a mutation
125-
queryCache.invalidateQueries({ key: ['todos'], exact: true })
123+
queryCache.invalidateQueries({ key: todosQuery.key, exact: true })
126124
},
127125
128126
onError(err, todo, { oldTodos, newTodos }) {
129127
// oldTodos can be undefined if onMutate errors
130-
if (newTodos != null && newTodos === queryCache.getQueryData(['todos'])) {
131-
queryCache.setQueryData(['todos'], oldTodos)
128+
if (
129+
newTodos != null
130+
&& newTodos === queryCache.getQueryData(todosQuery.key)
131+
) {
132+
queryCache.setQueryData(todosQuery.key, oldTodos)
132133
}
133134
134135
console.error(err)
@@ -140,28 +141,28 @@ const { mutate: deleteTodo } = useMutation({
140141
mutation: (todo: Todo) => $fetch(`/api/todos/${todo.id}`, { method: 'DELETE' }),
141142
142143
onMutate(todo) {
143-
const oldTodos = queryCache.getQueryData<Todo[]>(['todos']) || []
144+
const oldTodos = queryCache.getQueryData(todosQuery.key) || []
144145
const todoIndex = oldTodos.findIndex(t => t.id === todo.id)
145146
let newTodos = oldTodos
146147
if (todoIndex >= 0) {
147148
newTodos = oldTodos.toSpliced(todoIndex, 1)
148-
queryCache.setQueryData(['todos'], newTodos)
149+
queryCache.setQueryData(todosQuery.key, newTodos)
149150
}
150151
151-
queryCache.cancelQueries({ key: ['todos'], exact: true })
152+
queryCache.cancelQueries({ key: todosQuery.key, exact: true })
152153
153154
return { oldTodos, newTodos }
154155
},
155156
156157
onSettled() {
157158
// always refetch the todos after a mutation
158-
queryCache.invalidateQueries({ key: ['todos'], exact: true })
159+
queryCache.invalidateQueries({ key: todosQuery.key, exact: true })
159160
},
160161
161162
onError(err, todo, { oldTodos, newTodos }) {
162163
// oldTodos can be undefined if onMutate errors
163-
if (newTodos != null && newTodos === queryCache.getQueryData(['todos'])) {
164-
queryCache.setQueryData(['todos'], oldTodos)
164+
if (newTodos != null && newTodos === queryCache.getQueryData(todosQuery.key)) {
165+
queryCache.setQueryData(todosQuery.key, oldTodos)
165166
}
166167
167168
console.error(err)

app/pages/todos.vue

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import { todosQuery } from '~/queries/todos'
3+
24
definePageMeta({
35
middleware: 'auth'
46
})
@@ -8,14 +10,7 @@ const newTodoInput = useTemplateRef('new-todo')
810
const toast = useToast()
911
const queryCache = useQueryCache()
1012
11-
const { data: todos } = useQuery({
12-
key: ['todos'],
13-
// NOTE: the cast sometimes avoids an "Excessive depth check" TS error
14-
// using $fetch directly doesn't avoid the round trip to the server
15-
// when doing SSR
16-
// https://github.com/nuxt/nuxt/issues/24813
17-
query: () => useRequestFetch()('/api/todos') as Promise<Todo[]>
18-
})
13+
const { data: todos } = useQuery(todosQuery)
1914
2015
const { mutate: addTodo, isLoading: loading } = useMutation({
2116
mutation: (title: string) => {
@@ -31,7 +26,7 @@ const { mutate: addTodo, isLoading: loading } = useMutation({
3126
},
3227
3328
async onSuccess(todo) {
34-
await queryCache.invalidateQueries({ key: ['todos'] })
29+
await queryCache.invalidateQueries(todosQuery)
3530
toast.add({ title: `Todo "${todo.title}" created.` })
3631
},
3732
@@ -73,7 +68,7 @@ const { mutate: toggleTodo } = useMutation({
7368
}),
7469
7570
async onSuccess() {
76-
await queryCache.invalidateQueries({ key: ['todos'] })
71+
await queryCache.invalidateQueries(todosQuery)
7772
}
7873
})
7974
@@ -82,7 +77,7 @@ const { mutate: deleteTodo } = useMutation({
8277
$fetch(`/api/todos/${todo.id}`, { method: 'DELETE' }),
8378
8479
async onSuccess(_result, todo) {
85-
await queryCache.invalidateQueries({ key: ['todos'] })
80+
await queryCache.invalidateQueries(todosQuery)
8681
toast.add({ title: `Todo "${todo.title}" deleted.` })
8782
}
8883
})

app/queries/todos.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineQueryOptions } from '@pinia/colada'
2+
3+
export const todosQuery = defineQueryOptions({
4+
key: ['todos'],
5+
// NOTE: the cast sometimes avoids an "Excessive depth check" TS error
6+
// using $fetch directly doesn't avoid the round trip to the server
7+
// when doing SSR
8+
// https://github.com/nuxt/nuxt/issues/24813
9+
query: () => useRequestFetch()('/api/todos') as Promise<Todo[]>
10+
})

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"@iconify-json/simple-icons": "^1.2.31",
1414
"@nuxt/ui": "^3.0.2",
1515
"@nuxthub/core": "^0.8.24",
16-
"@pinia/colada": "^0.14.2",
17-
"@pinia/colada-nuxt": "^0.1.1",
16+
"@pinia/colada": "^0.16.1",
17+
"@pinia/colada-nuxt": "^0.2.0",
1818
"@pinia/nuxt": "^0.11.0",
1919
"drizzle-kit": "^0.30.6",
2020
"drizzle-orm": "0.41.0",
@@ -34,7 +34,9 @@
3434
"packageManager": "[email protected]",
3535
"pnpm": {
3636
"onlyBuiltDependencies": [
37+
"esbuild",
3738
"sharp",
39+
"vue-demi",
3840
"workerd"
3941
]
4042
}

0 commit comments

Comments
 (0)