Silgi

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:

MethodReturnsDescription
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:

MethodReturnsDescription
key(opts?)EntryKeyKey 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

On this page