Can you use useFetch twice in one composable, once on server, once in client?

I am trying to use useFetch() once on the server and a second time in the client, in one composable. So far I have not been able to get this to work. A reproduction of my attempt can be found here: https://stackblitz.com/edit/github-o9ivmk?file=composables%2FuseTwoStepFetch.ts It is the second useFetch() with server:false that I cannot get to return something -- it always returns null. Is there a way to get this working? An example somewhere? The composable looks like this:
// Simplified example.
// Fetch data in two steps:
// 1. Quickly get a small number of records (3)
// 2. Get all the rest of the data
export async function useTwoStepFetch() {
const nuxtApp = useNuxtApp();
const result = ref([] as any[]);

// get the first 3 records
// use useFetch() so this call can be done on the server
const body1 = { take: 3 };
await nuxtApp.runWithContext(async () => {
const { data }: any = await useFetch('/api/hello', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: body1,
watch: false,
server: true,
lazy: false,
});

// Put data in the result
console.log('data.value (1)', data.value);
result.value = data.value;
});

// get the rest of the data
// ** HERE I WANT TO BE SURE THAT IT RUNS ONLY ON CLIENT **
const body2 = { skip: 3 };
// ** NO AWAIT BECAUSE WE WANT TO RETURN THE RESULT BEFORE THE CALL FINISHED **
nuxtApp.runWithContext(async () => {
const { data }: any = await useFetch('/api/hello', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: body2,
watch: false,
/* server:true, lazy:false works, but that is not what I want */
server: false,
lazy: true,
});

console.log('data.value (2)', data.value);
result.value.splice(result.value.length, 0, ...(data.value || []));
});

return result;
}
// Simplified example.
// Fetch data in two steps:
// 1. Quickly get a small number of records (3)
// 2. Get all the rest of the data
export async function useTwoStepFetch() {
const nuxtApp = useNuxtApp();
const result = ref([] as any[]);

// get the first 3 records
// use useFetch() so this call can be done on the server
const body1 = { take: 3 };
await nuxtApp.runWithContext(async () => {
const { data }: any = await useFetch('/api/hello', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: body1,
watch: false,
server: true,
lazy: false,
});

// Put data in the result
console.log('data.value (1)', data.value);
result.value = data.value;
});

// get the rest of the data
// ** HERE I WANT TO BE SURE THAT IT RUNS ONLY ON CLIENT **
const body2 = { skip: 3 };
// ** NO AWAIT BECAUSE WE WANT TO RETURN THE RESULT BEFORE THE CALL FINISHED **
nuxtApp.runWithContext(async () => {
const { data }: any = await useFetch('/api/hello', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: body2,
watch: false,
/* server:true, lazy:false works, but that is not what I want */
server: false,
lazy: true,
});

console.log('data.value (2)', data.value);
result.value.splice(result.value.length, 0, ...(data.value || []));
});

return result;
}
H.J. van Meerveld
StackBlitz
Nuxt - Starter - StackBlitz
Create a new Nuxt project, module, layer or start from a theme with our collection of starters.
4 Replies
Hendrik Jan
Hendrik JanOP9mo ago
I guess you are right in that, but I don't really understand how to do it different. Isn't the await (before the second useFetch() making sure that we are waiting until the data is ready? I try to have the first part of my composable to take place on the server and the second part to take place on the client. I also tried hooks, but to no avail yet. Ah, I'll have to read about lazy in the documentation once again then. Thank you so much for taking time to answer. I'll have to make a study about this. I see the change you made, but it will need some time for me to understand 😅 . Actually your example also has lazy:true same as mine -- or am I looking at the wrong place? Oh, wait, you are using $fetch -- but then, can I be sure this will only be run in the client? $fetch doesn't deduplicate so the call could potentially happen both on the server and in the client, which I don't want. Oh, I think I'm getting at it. If is set server:false, lazy:false for the second useFetch(), the useFetch() will be run twice (once on server, once on client), but the first (on server) returns "null" and the second (on client) return "[3,4,5,6,7,8,9]". I think I'll be able to get this working in the non-simplified version at work :-).
Hendrik Jan
Hendrik JanOP9mo ago
H.J. van Meerveld
StackBlitz
Nuxt - Starter (forked) - StackBlitz
Create a new Nuxt project, module, layer or start from a theme with our collection of starters.
Hendrik Jan
Hendrik JanOP9mo ago
Oh, is seems to do it for me. What is wrong do you think? When I click the refresh-button I first see "[0,1,2]" (which I think is rendered on the server) and after a fraction of a second this is changed to "[0,1,2,3,4,5,6,7,8,9]", so it seems to work. I changed "lazy:true" to "lazy:false", zo the data is ready right away as I understand it. :-).
Hendrik Jan
Hendrik JanOP9mo ago
In case someone else wants to solve a similar problem, here is a working reproduction with some accompanying text: https://stackblitz.com/edit/github-o9ivmk-qkcdj8?file=app.vue
H.J. van Meerveld
StackBlitz
Nuxt - Double useFetch in composable (forked) - StackBlitz
Create a new Nuxt project, module, layer or start from a theme with our collection of starters.
Want results from more Discord servers?
Add your server