Data Fetching
Kimesh integrates with TanStack Query to provide powerful data loading patterns. Data can be prefetched in route loaders so it's ready before navigation completes.
The Pattern
- Define query options in a data file
- Prefetch in the route loader with
queryClient.ensureQueryData() - Use
useQuery()in the component — data is already cached
Quick Example
Define Queries
ts
// routes/posts/-posts.data.ts
import { defineQueryOptions, createQueryKeyFactory } from '@kimesh/query'
export const postKeys = createQueryKeyFactory('posts', {
list: null,
detail: (id: string) => id,
})
export const postsListQuery = defineQueryOptions({
key: postKeys.list(),
query: () => fetch('/api/posts').then(r => r.json()),
staleTime: 5 * 60 * 1000,
})
export const postDetailQuery = (id: string) =>
defineQueryOptions({
key: postKeys.detail(id),
query: () => fetch(`/api/posts/${id}`).then(r => r.json()),
})Prefetch in Loader
vue
<!-- routes/posts/index.vue -->
<script lang="ts">
import { postsListQuery } from './-posts.data'
// createFileRoute is auto-imported
export const Route = createFileRoute('/posts')({
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(postsListQuery)
},
})
</script>
<script setup lang="ts">
import { postsListQuery } from './-posts.data'
// useQuery is auto-imported — data already cached!
const { data: posts } = useQuery(postsListQuery)
</script>
<template>
<article v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
</article>
</template>Nuxt-style Composables
Kimesh also provides Nuxt-style data fetching composables:
useKmFetch
vue
<script setup>
// Auto-imported
const { data, pending, error, refresh } = useKmFetch('/api/posts')
</script>useKmAsyncData
vue
<script setup>
const { data, pending, error } = useKmAsyncData('posts', () =>
fetch('/api/posts').then(r => r.json())
)
</script>Lazy Variants
useLazyKmFetch and useLazyKmAsyncData don't block navigation — they fetch in the background.
$fetch
The global $fetch utility provides a layer-aware fetch client:
ts
// Auto-imported
const posts = await $fetch('/api/posts')
const post = await $fetch('/api/posts/1', {
method: 'POST',
body: { title: 'New Post' },
})Suspense & Deferred Loading
For progressive loading with skeleton UI:
ts
import { defer } from '@kimesh/query'
export const Route = createFileRoute('/users/:id')({
loader: async ({ params, context }) => {
// Critical: blocks navigation
await context.queryClient.ensureQueryData(userQuery(params.id))
// Deferred: loads progressively
defer(context.queryClient, userActivityQuery(params.id))
},
})vue
<template>
<!-- Renders immediately -->
<UserHeader :user="user" />
<!-- Progressive loading with KmDeferred -->
<KmDeferred>
<UserActivity :user-id="user.id" />
<template #fallback>
<ActivitySkeleton />
</template>
<template #error="{ error, retry }">
<p>{{ error.message }}</p>
<button @click="retry">Retry</button>
</template>
</KmDeferred>
</template>Next Steps
- Data Loading Guide — Complete guide with all patterns and strategies
- State Management — Manage reactive state
useKmFetchAPI — Full API reference