N
Nuxtā€¢8mo ago
Malik

What is "The Nuxt Way" (TM) to abstract http libs?

Hello friendly people of Nuxt, time is valuable, so I get straight to the point. I'd like to abstract a http library for global usage in a fresh nuxt application. Let's say axios, or fetch, or capacitorHttp, or whatever. Basically creating an instance with predetermined properties like the base url of an api and some headers. Base url being read from the .env file, proxied by the runtime Config. First approach was a custom nuxt plugin, but that did not seem to be usable within the pinia store. Second approach was a composable, but that failed because I got no access to the runtime Config from within the composable. Is what I am trying to achieve so out of this world? Please tell me I get a major knot in my brain and either what I try to do is dumb because of reason x, or the right way to do this is y, or I just made a stupid mistake z. That would be great šŸ˜„ Super bare-bones abstraction example with axios:
import axios from "axios";
import AuthModule from '@/repository/auth';

interface Api {
export default defineNuxtPlugin(() => {
const runtimeConfig = useRuntimeConfig();
const apiBaseUrl = runtimeConfig.app.apiUrl;

const axiosInstance = axios.create({
baseURL: apiBaseUrl as string,
});

axiosInstance.interceptors.request.use(
...
);

axiosInstance.interceptors.response.use(
...
);

const modules: Api = {
auth: new AuthModule(axiosInstance),
};

return {
provide: {
api: modules
},
};
});
import axios from "axios";
import AuthModule from '@/repository/auth';

interface Api {
export default defineNuxtPlugin(() => {
const runtimeConfig = useRuntimeConfig();
const apiBaseUrl = runtimeConfig.app.apiUrl;

const axiosInstance = axios.create({
baseURL: apiBaseUrl as string,
});

axiosInstance.interceptors.request.use(
...
);

axiosInstance.interceptors.response.use(
...
);

const modules: Api = {
auth: new AuthModule(axiosInstance),
};

return {
provide: {
api: modules
},
};
});
Thank you kindly for every hint you could give.
9 Replies
manniL
manniLā€¢8mo ago
I personally would do it similarly but move the axios / fetch creation in an extra function
manniL
manniLā€¢8mo ago
Alexander Lichter
YouTube
Custom $fetch and Repository Pattern in Nuxt 3
šŸ’» The repository pattern is a popular way to abstract your API calls away and provide a descriptive way to retrieve or send data. But how would you implement it in Nuxt? And should you use composables or $fetch for it? This video will give answers to all the questions! Key points: šŸŽ›ļø Implementing the repository pattern in Nuxt šŸ”› Creating our ow...
Malik
MalikOPā€¢8mo ago
Thank you @manniL / TheAlexLichter for the super fast support :). I figure that you use a similar approach as I had in mind with my setup, but if I didn't miss it, I see no usage of your plugin from within a pinia store. While I am not necessarily saying that it is generally my preferred approach to make http requests from store actions, I see it quite a lot. My custom http plugin approach, however, was not usable from within a pinia store file. Do you have a suggestion for that? I believe what bites me is the access to the runtime Config and the wrong context I am trying to access it in. EDIT: If the answer is "making http requests in store actions is an anti-pattern" then I could live with that, too šŸ˜„
manniL
manniLā€¢8mo ago
No problem! šŸ‘ Yes, I didn't use it in pinia there but let me spin it up an example for it
manniL
manniLā€¢8mo ago
brief idea
Malik
MalikOPā€¢8mo ago
Uh nice!!! I am not sure if I get that correctly, but what solves this is that using a function instead of an object when calling defineStore and inside that function I get access to useNuxtApp(), am I right? Just to understand why this works now šŸ˜„ Changing your example to what I had before
import type { User } from '~/utils/repository.js';
const app = useNuxtApp();
const userRepo = repository(app.$api);

export const useTestStore = defineStore('test', {
state: () => ({
users: [] as User[],
}),
actions: {
async fetchData() {
this.users = await userRepo.get();
},
},
});
import type { User } from '~/utils/repository.js';
const app = useNuxtApp();
const userRepo = repository(app.$api);

export const useTestStore = defineStore('test', {
state: () => ({
users: [] as User[],
}),
actions: {
async fetchData() {
this.users = await userRepo.get();
},
},
});
Results in what I struggled with
[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables`.
[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables`.
Oh my, I should have thought of that. Well, thanks a bunch @manniL / TheAlexLichter, you just solved my case. I am flabbergasted.
manniL
manniLā€¢8mo ago
Yes, setup stores are the way to go there šŸ™ŒšŸ» If you use useNuxtApp outside of defineStore, then it will never have the context. Same for other composables
Want results from more Discord servers?
Add your server