createUseFetch v4.2

Source
A factory function to create a custom useFetch composable with pre-defined default options.

createUseFetch creates a custom useFetch composable with pre-defined options. The resulting composable is fully typed and works exactly like useFetch, but with your defaults baked in.

createUseFetch is a compiler macro. It must be used as an exported declaration in the composables/ directory (or any directory scanned by the Nuxt compiler). Nuxt automatically injects de-duplication keys at build time.

Usage

app/composables/useAPI.ts
export const useAPI = createUseFetch({
  baseURL: 'https://api.nuxt.com',
})
app/pages/modules.vue
<script setup lang="ts">
const { data: modules } = await useAPI('/modules')
</script>

The resulting useAPI composable has the same signature and return type as useFetch, with all options available for the caller to use or override.

Type

Signature
function createUseFetch (
  options?: Partial<UseFetchOptions>,
): typeof useFetch

function createUseFetch (
  options: (callerOptions: UseFetchOptions) => Partial<UseFetchOptions>,
): typeof useFetch

// where the client declares the routes it serves
function createUseFetch<Routes> (
  options: Partial<UseFetchOptions> & { routes: Routes },
): DeclaredUseFetch<Routes>

Options

createUseFetch accepts all the same options as useFetch, including baseURL, headers, query, onRequest, onResponse, server, lazy, transform, getCachedData, and more.

See the full list of options in the useFetch documentation.

Typing a Third-Party API

By default a composable created with createUseFetch is typed from the routes your own server serves, so a request to another API resolves to unknown. Pass routes to say what that API serves, and every request the composable makes is resolved against it instead:

app/composables/usePetStore.ts
import type { DynamicParam, Endpoint } from 'nuxt/app'

interface Pet { id: number, name: string }

interface PetStoreRoutes {
  '/pets': {
    [Endpoint]: {
      GET: { response: Pet[], query: { limit?: number } }
      POST: { response: Pet, body: { name: string } }
    }
    // a path parameter, matched positionally
    [DynamicParam]: {
      [Endpoint]: { GET: { response: Pet } }
    }
  }
}

export const usePetStore = createUseFetch({
  baseURL: 'https://api.example.com',
  routes: {} as PetStoreRoutes,
})
const { data: pets } = await usePetStore('/pets')
//      ^? Pet[]
const { data: pet } = await usePetStore('/pets/42')
//      ^? Pet
await usePetStore('/pets', { method: 'post', body: { name: 'Rex' } })

await usePetStore('/pats')
//                ^ no GET route matches '/pats'
await usePetStore('/pets', { method: 'put' })
//                          ^ no PUT route matches '/pets'
await usePetStore('/pets', { method: 'post' })
//                          ^ body is required

Only the type of routes is read, so pass {} as Routes; the value is dropped before the request is made. The declared paths are matched as written, since they are the paths the API documents. You should not prefix them with the baseURL. A path built at runtime resolves to unknown.

The routes a client declares are its own. They are not added to your app's route set, so plain $fetch and useFetch are unaffected, and this client will not accept your own server's paths.
The interface above is the shape fetchdts uses, which is what Nuxt generates for your own server routes. A module can therefore generate one from an API description - an OpenAPI document, for example - with compileRoutes from fetchdts/compiler, and hand the emitted interface to routes.

Default vs Override Mode

Default Mode (plain object)

When you pass a plain object, the factory options act as defaults. Callers can override any option:

app/composables/useAPI.ts
export const useAPI = createUseFetch({
  baseURL: 'https://api.nuxt.com',
  lazy: true,
})
// Uses the default baseURL
const { data } = await useAPI('/modules')

// Caller overrides the baseURL
const { data } = await useAPI('/modules', { baseURL: 'https://other-api.com' })

Override Mode (function)

When you pass a function, the factory options override the caller's options. The function receives the caller's options as its argument, so you can read them to compute your overrides:

app/composables/useAPI.ts
// baseURL is always enforced, regardless of what the caller passes
export const useAPI = createUseFetch(callerOptions => ({
  baseURL: 'https://api.nuxt.com',
}))

This is useful for enforcing settings like authentication headers or a specific base URL that should not be changed by the caller.

Combining with a Custom $fetch

You can pass a custom $fetch instance to createUseFetch:

app/composables/useAPI.ts
export const useAPI = createUseFetch(callerOptions => ({
  $fetch: useNuxtApp().$api as typeof $fetch,
  ...callerOptions,
}))
The function signature (override mode) is required here so that useNuxtApp() is called in the setup context (at the composable call site) rather than in the module scope, where no Nuxt instance is available.
Read more in Docs > Guide > Recipes > Custom Usefetch.
Read more in Docs > API > Composables > Use Fetch.