zenstack-pinia-colada
v0.1.5
Published
Pinia Colada Client for consuming ZenStack v3's CRUD service
Maintainers
Readme
ZenStack Pinia Colada
Pinia Colada client for ZenStack - The Smart Data Fetching Layer for Vue 3.
What is ZenStack?
ZenStack is a toolkit that supercharges Prisma ORM with a powerful access control layer and unleashes its full potential for full-stack development. It allows you to write authorization logic in your database schema and generates type-safe CRUD APIs automatically.
What is Pinia Colada?
Pinia Colada is the smart data fetching layer for Vue 3, built on top of Pinia. It provides declarative data fetching with automatic caching, request deduplication, and optimistic updates.
Features
- 🔐 Type-safe - Full TypeScript support with automatic type inference
- ⚡️ Automatic caching - Smart caching powered by Pinia Colada
- 🔄 Optimistic updates - Update UI before server responds
- 🎯 Automatic invalidation - Cache invalidation based on data relationships
- 📦 Zero config - Works out of the box with your ZenStack schema
- 🌳 Tree-shakeable - Only bundle what you use
Installation
npm install zenstack-pinia-colada @pinia/colada pinia
# or
pnpm add zenstack-pinia-colada @pinia/colada pinia
# or
yarn add zenstack-pinia-colada @pinia/colada piniaPrerequisites
- You need a ZenStack project set up. See ZenStack documentation for details.
- Generate your ZenStack schema using
npx zenstack generate
Quick Start
1. Setup Pinia Colada
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { PiniaColada } from '@pinia/colada'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(PiniaColada)
app.mount('#app')2. Provide Query Settings (Optional)
<!-- App.vue or layout component -->
<script setup lang="ts">
import { provideQuerySettingsContext } from 'zenstack-pinia-colada'
provideQuerySettingsContext({
endpoint: '/api/model', // default endpoint
logging: true, // enable logging for debugging
})
</script>3. Use in Components
<script setup lang="ts">
import { useClientQueries } from 'zenstack-pinia-colada'
import { schema } from './zenstack/schema-lite'
const queries = useClientQueries(schema)
// Query data
const { data: users, status, error } = queries.user.useFindMany()
// Mutations
const createUser = queries.user.useCreate()
const handleCreateUser = () => {
createUser.mutate({
data: {
email: '[email protected]',
name: 'John Doe'
}
})
}
</script>
<template>
<div>
<div v-if="status === 'pending'">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<ul v-else>
<li v-for="user in users" :key="user.id">
{{ user.name }} ({{ user.email }})
</li>
</ul>
<button
@click="handleCreateUser"
:disabled="createUser.status === 'pending'"
>
Create User
</button>
</div>
</template>API Reference
Query Hooks
Each model in your schema gets the following query hooks:
useFindUnique(args, options)- Find a unique recorduseFindFirst(args, options)- Find the first matching recorduseFindMany(args, options)- Find multiple recordsuseInfiniteFindMany(args, options)- Paginated query with infinite loadinguseCount(args, options)- Count recordsuseAggregate(args, options)- Aggregate datauseGroupBy(args, options)- Group records
Query Return Values:
{
data: Ref<T | undefined>, // Query data
error: Ref<Error | null>, // Error if query failed
status: Ref<'pending' | 'success' | 'error'>, // Query status
refresh: () => Promise<void>, // Manually refetch
// ... and more from Pinia Colada
}Mutation Hooks
Each model gets these mutation hooks:
useCreate(options)- Create a recorduseCreateMany(options)- Create multiple recordsuseCreateManyAndReturn(options)- Create and return multiple recordsuseUpdate(options)- Update a recorduseUpdateMany(options)- Update multiple recordsuseUpdateManyAndReturn(options)- Update and return multiple recordsuseUpsert(options)- Create or update a recorduseDelete(options)- Delete a recorduseDeleteMany(options)- Delete multiple records
Mutation Return Values:
{
mutate: (variables: T) => void, // Trigger mutation
mutateAsync: (variables: T) => Promise<R>, // Async mutation
status: Ref<'pending' | 'success' | 'error' | 'idle'>,
data: Ref<R | undefined>, // Mutation result
error: Ref<Error | null>, // Error if mutation failed
// ... and more from Pinia Colada
}Advanced Features
Optimistic Updates
Optimistic updates allow the UI to update immediately before the server responds:
const updatePost = queries.post.useUpdate({
optimisticUpdate: true, // Enable optimistic updates
})
updatePost.mutate({
where: { id: '1' },
data: { title: 'New Title' }
})
// UI updates immediately, then syncs with server responseCustom Query Options
Pinia Colada provides many options to customize query behavior:
const { data } = queries.post.useFindMany(
{ where: { published: true } },
{
staleTime: 5000, // Consider data fresh for 5 seconds
gcTime: 300000, // Garbage collection time (default: 5 minutes)
refetchOnMount: true,
refetchOnWindowFocus: true,
enabled: computed(() => isReady.value), // Conditionally enable
}
)Infinite Queries (Pagination)
For paginated data with infinite scrolling:
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
queries.post.useInfiniteFindMany(
{ take: 10, where: { published: true } },
{
getNextPageParam: (lastPage, pages) => {
// Return the cursor for the next page
return lastPage.length === 10 ? pages.length * 10 : undefined
}
}
)Disable Auto Invalidation
By default, mutations automatically invalidate related queries. You can disable this:
const createPost = queries.post.useCreate({
invalidateQueries: false, // Don't auto-invalidate related queries
})Type Safety
All hooks are fully typed based on your ZenStack schema:
// TypeScript knows the exact shape of User
const { data: user } = queries.user.useFindUnique({
where: { id: '1' },
select: { id: true, name: true, email: true }
})
// user is typed as: { id: string; name: string; email: string } | nullComparison with TanStack Query
If you're familiar with @zenstackhq/tanstack-query, the Pinia Colada client offers:
- 🎯 Vue-first design - Built specifically for Vue 3 composition API
- 📦 Smaller bundle - Tree-shakeable ESM-only package
- 🔧 Simpler API - Less configuration, sensible defaults
- 🏪 Pinia integration - Works seamlessly with your Pinia store
- ⚡️ Better performance - Optimized for Vue's reactivity system
Key API Differences:
- Returns Vue
Refobjects instead of plain values - Uses
statusinstead of separateisLoading,isSuccessflags refresh()instead ofrefetch()for manual updates- Direct integration with Pinia's state management
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
