$fetch

The Universal Fetch — In Handlers, Stores, Server Routes

$fetch

$fetch is ofetch under the hood — it works on server and client, parses JSON automatically, and throws on non-2xx status codes.

4 min read Level 2/5 #nuxt#fetching#fetch
What you'll learn
  • Use $fetch from button handlers and Pinia actions
  • Pass method, body, headers, and query options
  • Handle errors with try/catch

useFetch and useAsyncData are for page-level data. For everything else — handlers, actions, server routes — use $fetch.

Basic Calls

// GET
const user = await $fetch(`/api/users/${id}`)

// POST with body — auto JSON-stringified
const created = await $fetch('/api/users', {
  method: 'POST',
  body: { name: 'Ada' }
})

// Query string
const list = await $fetch('/api/posts', { query: { page: 2, tag: 'js' } })

Inside Click Handlers

<script setup lang="ts">
const name = ref('')

async function submit() {
  try {
    await $fetch('/api/users', { method: 'POST', body: { name: name.value } })
    name.value = ''
  } catch (err: any) {
    console.error(err.data ?? err)
  }
}
</script>

<template>
  <input v-model="name" />
  <button @click="submit">Create</button>
</template>

Errors Throw

Unlike the native fetch, $fetch throws on any non-2xx response. The thrown error has a data property containing the parsed body — useful for showing server-provided error messages.

Raw Responses

When you need headers or status, use $fetch.raw:

const res = await $fetch.raw('/api/posts')
console.log(res.status, res.headers.get('x-total-count'))
const body = res._data

$fetch also works inside server/api/*.ts handlers — it has the same shape on both sides of the wire.

server/api Routes →