nuxt-open-fetch
nuxt-open-fetch copied to clipboard
Use request fetch on SSR
Hi,
we are proxying our API through a nuxt catch-all server route. The server route authenticates the API call based on the user's session.
On SSR, this works fine with useFetch(), as it falls back to useRequestFetch() for local calls on the server side:
https://github.com/nuxt/nuxt/blob/a80d1a0d6349bf1003666fc79a513c0d7370c931/packages/nuxt/src/app/composables/fetch.ts#L170-L176
Since the $fetch instance in nuxt-open-fetch is always globalThis.$fetch via createOpenFetch, the above behaviour cannot be used and thus use[Client] behaves differently from useFetch for SSR.
Am I correctly assuming that this can be achieved by basically copying the above snippet to select the $fetch instance into createOpenFetch and would you be open to such a PR?
Thanks
Hi @philschleier,
Good catch! Yes, PR would be great :)
@enkot is this something you can implement? I'm seeing different behavior in SSR as well and it's a blocker in using the package 😬
I tried manually providing the useRequestFetch to the $fetch option; however, it does not properly perform the path replacements
@philschleier @adamdehaven Should be fixed in https://github.com/enkot/nuxt-open-fetch/releases/tag/v0.9.0 :) Please, let me know if you have any problem.
I'll definitely check it out, thanks!
@enkot looking at your updated docs example:
export default defineNuxtPlugin({
enforce: 'pre', // clients will be ready to use by other plugins, Pinia stores etc.
setup() {
const clients = useRuntimeConfig().public.openFetch
const localFetch = useRequestFetch()
return {
provide: Object.entries(clients).reduce((acc, [name, client /* should this be options instead? */ ]) => ({
...acc,
[name]: createOpenFetch(localOptions => ({
...options,
...localOptions,
onRequest(ctx) {
console.log('My logging', ctx.request)
return localOptions?.onRequest?.(ctx)
}
}), localFetch)
}), {})
}
}
})
Should client (extracted from the array) actually be options as it was previously? Otherwise, I'm not sure where ...options come from in the new example? (Please double-check me, maybe I'm missing something)
I locally switched my configuration to options as suggested above and everything seems to be working correctly for me (so I think just the docs example needs to be fixed)
@enkot I'm also noticing that my NuxtApp interface does not automatically pick up my custom client (or any clients).
I tried manually adding the interface but this cannot be resolved when I actually run the build:
import type { paths as ApiPaths } from '#open-fetch-schemas/api'
Error:
Cannot find module '#open-fetch-schemas/api' or its corresponding type declarations.
What Nuxt version do you use? Looks like it doesn't resolve on Nuxt 3.11.2 for some reason, but works fine on 3.12.2
I'm on 3.12.2 and it's not working for me 😬
I should clarify: when I run prepare or dev it works fine; however, when I run build it errors.
Edit: I'm also using Nuxt layers in a mono repository; unsure if that's related
I'm also having to manually declare the $api interface because it doesn't seem to be automatically added for me?
// nuxt-app.d.ts
import type { paths as ApiPaths } from '#open-fetch-schemas/api'
import type { OpenFetchClient } from '#imports'
declare module '#app' {
interface NuxtApp {
$api: OpenFetchClient<ApiPaths>
}
}
@adamdehaven Have you tried deleting the lock file and reinstalling deps?? Maybe @nuxt/kit has a different version than Nuxt.
If this doesn't work, could you create a small repro?
~~I have tried that, but I can also try manually bumping nuxtkit.~~
Edit: @nuxt/kit is up to date.
The only other difference is my custom client plugin is not enforce: 'pre' but I don't think that should be an issue.
I'm in a mono repo that also uses layers.
I opened this issue (as I've seen the problem several times now): https://github.com/nuxt/nuxt/issues/27693
You could try exporting one extra interface like I outline in the ticket, but unsure if that's an actual solution or just a workaround.
@enkot using a mono repo + layers setup like referenced above, I can't get past the error shown below:
When I run the dev server everthing is fine; however, when I run the build, my project is choking on the aliased import paths 😢
I think I may have figured out the issue 😅
If I create an alias in my nuxt.config.ts as shown here, the file properly resolves for the build:
alias: {
'#open-fetch-schemas/portal-api': fileURLToPath(new URL('./.nuxt/types/open-fetch/schemas/portal-api.ts', import.meta.url)),
}
I believe the issue is the paths exported for tsconfig alias are incorrect, and that the file containing the types (here as portal-api.ts) that the module generates for each client is *.ts instead of *.d.ts so the import path ends up being incorrect.
I pushed up a PR here that attempts to resolve the issue.
Not sure if it's related to this issue (I'm probably doing something silly since I'm new to nuxt-open-fetch) but I'm having problems with SSR using nuxt-open-fetch 0.9.0.
This code server-side renders fine without any hydration errors:
<template>
{{ data }}
</template>
<script setup lang="ts">
const data = ref<any>(null);
// SSR ✅; Ny hydration error ✅
data.value = (await useFetch("/api/test")).data;
</script>
However, this this code server-side renders (in that I can see the generated code when I view the document source) but flickers (there is a very brief moment when the document is empty). It also generates a hydration error (in which the client expects the data markup not to be rendered):
<template>
{{ data }}
</template>
<script setup lang="ts">
const data = ref<any>(null);
// SSR ✅; Hydration error ❌
data.value = (await useApi("/test")).data;
</script>
Oh, perhaps I should mention that I'm also using a catch-all server route.
server/api/[...].ts:
import { defineEventHandler, H3Event, parseCookies, setCookie } from "h3";
import { joinURL } from "ufo";
export default defineEventHandler(async (event: H3Event) => {
const cookies = parseCookies(event),
{ proxyUrl } = useRuntimeConfig(),
target = joinURL(proxyUrl, event.path),
token = cookies?.token;
setCookie(event, "token", token);
return proxyRequest(event, target);
});
I'm not sure what's going on here but I realized that an empty object options argument to useApi got rid of the error:
Without the object (code identical to above):
// SSR ✅; Hydration error ❌
data.value = (await useApi("/test")).data;
With the object:
// SSR ✅; No hydration error ✅
data.value = (await useApi("/test", {})).data;
It does't matter if the object is added or not if I get the client from useNuxtApp:
const { $api } = useNuxtApp();
data.value = await($api as any)("/test"); // SSR ✅; No hydration error ✅
const { $api } = useNuxtApp();
data.value = await($api as any)("/test", {}); // SSR ✅; No hydration error ✅
By the way, $api is of the type unknown for me. I'm using version 3.12.2 of both Nuxt and @nuxt/kit.