Skip to content

Use services from Vue

The module injects a Feathers client into your Nuxt application. You can call a service from a page, component, composable, or Pinia store.

Read a list

vue
<script setup lang="ts">
interface Message {
  id: string
  text: string
  createdAt: string
}

const { $api } = useNuxtApp()
const loading = ref(false)
const messages = ref<Message[]>([])

async function loadMessages(): Promise<void> {
  loading.value = true
  try {
    const result = await $api.service('messages').find({
      query: {
        $limit: 20,
        $sort: { createdAt: -1 },
      },
    })
    messages.value = Array.isArray(result) ? result : result.data
  }
  finally {
    loading.value = false
  }
}

await loadMessages()
</script>

<template>
  <p v-if="loading">Loading…</p>
  <ul v-else>
    <li v-for="message in messages" :key="message.id">
      {{ message.text }}
    </li>
  </ul>
</template>

Create data

ts
await $api.service('messages').create({
  text: 'Hello from Nuxt',
})

The server validates the payload with the service schema. Browser form validation is not a security boundary.

Listen to events

ts
const service = $api.service('messages')

function onCreated(message: Message): void {
  messages.value.unshift(message)
}

onMounted(() => service.on('created', onCreated))
onBeforeUnmount(() => service.off('created', onCreated))

Use authentication

ts
const auth = useAuth()

await auth.login({
  strategy: 'local',
  email: 'developer@example.test',
  password: 'test-password',
})

Use the generated session middleware for protected pages, or check auth.isAuthenticated before rendering a sensitive action.

Good practices

  • Type the data returned by each service.
  • Bound queries with $limit, $select, and validated filters.
  • Remove Feathers listeners when a component unmounts.
  • Put repeated calls in a composable or Pinia store.
  • Show a simple user-facing error and keep technical diagnostics separate.

nuxt-feathers-zod user documentation