Pinia actions can be asynchronous, making it easy to fetch data from APIs and manage loading and error states in a centralised store.
// src/stores/users.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUsersStore = defineStore('users', () => {
const users = ref([])
const loading = ref(false)
const error = ref(null)
async function fetchUsers() {
loading.value = true
error.value = null
try {
const response = await fetch('/api/users')
if (!response.ok) throw new Error('Failed to load users')
users.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
}
async function createUser(data) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
const newUser = await response.json()
users.value.push(newUser)
return newUser
}
return { users, loading, error, fetchUsers, createUser }
})
<script setup>
import { onMounted } from 'vue'
import { useUsersStore } from '@/stores/users'
const store = useUsersStore()
onMounted(() => store.fetchUsers())
</script>
<template>
<div v-if="store.loading">Loading...</div>
<div v-else-if="store.error">{{ store.error }}</div>
<ul v-else>
<li v-for="user in store.users" :key="user.id">{{ user.name }}</li>
</ul>
</template>