Pinia Colada
Generate type-safe queryOptions, mutationOptions, and cache keys for Vue with Pinia Colada.
Pinia Colada is a smart data-fetching layer for Vue built on top of Pinia. Silgi provides createColadaUtils, a utility that generates type-safe queryOptions and mutationOptions from your client — with full Vue reactivity support.
Prerequisites
You need a Silgi client set up. The Pinia Colada integration wraps this client.
bash pnpm add @pinia/colada pinia vue bash npm install @pinia/colada pinia vue bash bun add @pinia/colada pinia vue Setup
Create colada utils
import { } from 'silgi/pinia-colada'
import { } from './client' // your Silgi client
const = ()The utils object mirrors your client's structure. If your client has client.users.list, then utils.users.list gives you query/mutation helpers for that procedure.
Use in components
<script setup lang="ts">
import { useQuery } from '@pinia/colada'
const { data, status } = useQuery(
utils.users.list.queryOptions({
input: { limit: 10 },
}),
)
</script>
<template>
<p v-if="status === 'loading'">Loading...</p>
<ul v-else>
<li v-for="user in data" :key="user.id">{{ user.name }}</li>
</ul>
</template>Queries
queryOptions returns everything useQuery needs — a reactive computed key and the fetch function:
const = utils.users.list.queryOptions({
: { : 10 },
})
// options contains:
// - key: computed(() => [["users", "list"], { type: "query", input: { limit: 10 } }])
// - query: ({ signal }) => client.users.list({ limit: 10 }, { signal })Reactive inputs
Both input and context accept Vue reactive values — ref, computed, or getter functions:
import { , } from 'vue'
const = (10)
const = utils.users.list.queryOptions({
: (() => ({ : . })),
})When the reactive value changes, the query key updates automatically, which triggers a refetch.
Mutations
mutationOptions returns everything useMutation needs:
<script setup lang="ts">
import { useMutation, useQueryCache } from '@pinia/colada'
const queryCache = useQueryCache()
const { mutate, isLoading } = useMutation({
...utils.users.create.mutationOptions(),
onSuccess: () => {
queryCache.invalidateQueries({
key: utils.users.key(),
})
},
})
</script>
<template>
<button @click="mutate({ name: 'Alice' })" :disabled="isLoading">
{{ isLoading ? 'Creating...' : 'Create User' }}
</button>
</template>Cache invalidation
Every level of the utils tree has a key() method for generating cache keys:
import { } from '@pinia/colada'
const = ()
// Invalidate all queries under "users"
.({
: utils.users.key(),
})
// Invalidate a specific query with specific input
.({
: utils.users.list.key({ : 'query', : { : 10 } }),
})
// Invalidate everything
.({
: utils.key(),
})Direct calls
Each procedure util has a call property that is the original client function:
const = await utils.users.list.call({ : 10 })Available methods
Each procedure-level util provides:
| Method | Returns | Description |
|---|---|---|
queryOptions(opts?) | { key: ComputedRef, query, ... } | Full options for useQuery |
mutationOptions(opts?) | { key: fn, mutation, ... } | Full options for useMutation |
call(input, opts?) | Promise<TOutput> | Direct procedure call |
Each router-level util provides:
| Method | Returns | Description |
|---|---|---|
key(opts?) | EntryKey | Key prefix for bulk invalidation |
Input and context parameters accept MaybeRefOrGetter — meaning you can pass plain values, ref(), computed(), or
getter functions. The query key is a computed that updates reactively.
What's next?
- Client — set up the Silgi client that powers the colada utils
- TanStack Query — if you prefer TanStack Query over Pinia Colada
- Typed Errors — handle errors from your queries and mutations