N
Nuxt2mo ago
kyomi

does it make sense to create an api wrapper library as a nuxt module (or nuxt composable)?

to avoid xy problem: we have a front-end (nuxt) consuming some routes from our api. initially, we were using useFetch to consume these routes, but over time, multiple calls became repetitive and tiring, for example, a user route in the api (/users) generally returns the same payload format, you need to do error handling every time, you need to pass the baseURL every time to useFetch, that kind of thing. the idea was to create a library that is an API wrapper for our API, this library will abstract the calls and handle the responses appropriately to avoid redundant code in the front-end my question is: i started to create an api wrapper here using ofetch but my question is if this is the right way, should i use ofetch or should i create a nuxt module and use useFetch?
3 Replies
kapa.ai
kapa.ai2mo ago
Hi @kyomi. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, it absolutely makes sense to create an API wrapper as a Nuxt composable or even as a Nuxt module, especially to avoid repetitive code, centralize error handling, and manage things like base URLs and authentication headers in one place. Recommended Approach: - Use a Nuxt composable (e.g., useAPI) that wraps useFetch and injects your custom logic (base URL, headers, error handling, etc.). This is the most idiomatic and maintainable way in Nuxt 3/4. You can also expose a custom $fetch instance via a Nuxt plugin if you want even more control. - ofetch is the underlying library for $fetch in Nuxt, but using useFetch (or a wrapper around it) is preferred for SSR safety and to avoid double-fetching during hydration. Directly using ofetch or $fetch is fine for client-only calls, but for data that should be SSR-safe and hydrated, always use useFetch or useAsyncData (or your wrapper around them) see docs. How to do it: 1. Create a custom $fetch instance in a Nuxt plugin, where you can set the base URL, add headers, and handle errors globally. 2. Create a composable (e.g., useAPI) that wraps useFetch and injects your custom $fetch instance, so you can use it throughout your app without repeating options. Example from the official Nuxt docs:
// plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
const api = $fetch.create({
baseURL: '<https://api.example.com',>
onRequest({ options }) {
// Add auth headers, etc.
},
onResponseError({ response }) {
// Global error handling
}
})
return { provide: { api } }
})
// composables/useAPI.ts
import type { UseFetchOptions } from 'nuxt/app'
export function useAPI<T>(url: string, options?: UseFetchOptions<T>) {
return useFetch(url, {
...options,
$fetch: useNuxtApp().$api as typeof $fetch,
})
}
// plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
const api = $fetch.create({
baseURL: '<https://api.example.com',>
onRequest({ options }) {
// Add auth headers, etc.
},
onResponseError({ response }) {
// Global error handling
}
})
return { provide: { api } }
})
// composables/useAPI.ts
import type { UseFetchOptions } from 'nuxt/app'
export function useAPI<T>(url: string, options?: UseFetchOptions<T>) {
return useFetch(url, {
...options,
$fetch: useNuxtApp().$api as typeof $fetch,
})
}
Now you can use useAPI('/users') in your components, and all your custom logic is centralized see official recipe. Should you use a Nuxt module? - If your API wrapper is generic and reusable across multiple projects, a Nuxt module is a good idea. - For most projects, a composable (possibly with a plugin for the custom $fetch) is sufficient and idiomatic. Summary:
- Use a composable (and optionally a plugin) to wrap useFetch with your custom logic. - Prefer useFetch over direct ofetch for SSR safety and Nuxt integration. - Only make a Nuxt module if you need to share this logic across projects. References:
- Custom useFetch in Nuxt (official recipe) - useFetch API docs ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@802730467178577960> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
hiimmox
hiimmox2mo ago
At my job we created a plugin and exposed it using "provide" so we could do: context.$http

Did you find this page helpful?