Unable to use Nuxt with SSR and cookies
Opened this issue · 5 comments
Are you using Nuxt?
- Check this box if you encountered the issue using Nuxt and the included module.
Describe the bug
I have a Nuxt project, with pinia + pinia-plugin-persistedstate
I want to be able to use the cookies with SSR.
Pinia is used to store the auth token of the user, and refresh it if required.
I'm using the strore in a custom Nuxt plugin.
It works fine on the client side.
However, I realized that I have an issue if the server side tries to update the cookie through the Pinia store (eg. the new token is retrieve during SSR, and should be stored to the cookie to be transmitted to the client side).
Is it even possible?
I have the following issue when I try to update the store during SSR:
[1:04:47 PM] ERROR [pinia-plugin-persistedstate] [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.
at Module.useNuxtApp (node_modules/nuxt/dist/app/nuxt.js:251:13)
at Module.useRequestEvent (node_modules/nuxt/dist/app/composables/ssr.js:11:58)
at readRawCookies (node_modules/nuxt/dist/app/composables/cookie.js:144:101)
at Module.useCookie (node_modules/nuxt/dist/app/composables/cookie.js:29:19)
at Object.setItem (node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/storages.js:13:52)
at persistState (node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/core.js:42:13)
at store.$subscribe.detached (node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/core.js:73:29)
at node_modules/pinia/dist/pinia.mjs:1450:21
at callWithErrorHandling (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:200:19)
at callWithAsyncErrorHandling (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:207:17)
It is quite hard to write a reproduction, and it took me a lot of time to find the issue.
Reproduction
https://stackblitz.com/edit/nuxt-pinia-perstitedstate
System Info
System:
OS: Linux 5.15 Debian GNU/Linux 12 (bookworm) 12 (bookworm)
CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz
Memory: 9.23 GB / 15.52 GB
Container: Yes
Shell: 5.2.15 - /bin/bash
Binaries:
Node: 20.9.0 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.9.1 - /workspaces/[project]/node_modules/.bin/npm
pnpm: 8.10.2 - /usr/local/share/npm-global/bin/pnpm
Used Package Manager
npm
Validations
- Follow our Code of Conduct
- Read the Contributing Guide.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- The provided reproduction is a minimal reproducible of the bug.
I see a couple of potential issues/solutions.
Calling the store at the very root of your plugin makes it so the plugin might not be initialised yet (see #352), the store itself works because of how pinia can work outside of a Vue instance, but it will defer plugin initialisations.
You can try calling useAuthStore
in deeper scopes instead of at the root, this will defer the moment the store is retrieved, and may make it so the plugin is properly initialised.
For example :
async function signInWithToken(newToken: string) {
// call composable here, I dont think there needs to be a `runWithContext` or such
const authStore = useAuthStore()
try {
authStore.token = newToken;
await refreshUser();
} catch (e) {
authStore.token = undefined;
authStore.refreshToken = undefined;
}
}
This should make it work.
Next version will also expose an explicit name for this plugin (originally called pinia-plugin-persistedstate
) so you can also try adding dependsOn: ['pinia-plugin-persistedstate']
on your Nuxt plugin config, but I'm not sure this will really solve the issue.
Hi,
Thanks for your response.
I already tried to use the store directly in the functions, but I still get the same error.
I'm not sure how I can use the 'dependsOn', as this code is not in a plugin.
When does Pinia try to restore the cookies (what triggers it)?
I don't understand why the stores are called outside "a plugin, Nuxt hook, Nuxt middleware, or Vue setup function" as my code is an HTTP client, only used on Nuxt pages, plugins and so on. That's why I'm wondering when the cookies are "unpacked" into the store, to find what could possibly do it outside authorized scopes.
I will need a more complete reproduction to investigate, cause i cannot reproduce the error with what you sent in the original message.
Pinia restores cookies when the pinia plugin is initialised which only happens in Vue context.
@jeanmatthieud I have fixed this a long time ago. Pr was not merged. Have a look at #99
And #97 (comment)
Maybe this time @prazdevs may reconsider the PR since not only I have this issue.
I also having this issue, the only way it triggers the error is when the user is getting redirect back from Google auth... The issue doesn't trigger in any other navigation or situation for me...
So to suppress the issue for now, I cloned the repo and defined a static config for now, but I hope we find a better solution:
import type { Pinia, PiniaPluginContext } from 'pinia'
import { defineNuxtPlugin } from '#app'
import { destr } from 'destr'
import { createPersistence } from './core'
import { storages } from './storages'
function piniaPlugin(context: PiniaPluginContext) {
const options = {
storage: 'localStorage',
auto: false,
key: `%id`,
debug: false,
cookieOptions: {
sameSite: 'lax',
},
}
createPersistence(
context,
p => ({
key: options.key
? options.key.replace(/%id/g, p.key ?? context.store.$id)
: (p.key ?? context.store.$id),
debug: p.debug ?? options.debug ?? false,
serializer: p.serializer ?? {
serialize: data => JSON.stringify(data),
deserialize: data => destr(data),
},
storage: p.storage ?? (options.storage
? options.storage === 'cookies'
? storages.cookies(options.cookieOptions)
: storages[options.storage]()
: storages.cookies()),
beforeHydrate: p.beforeHydrate,
afterHydrate: p.afterHydrate,
pick: p.pick,
omit: p.omit,
}),
options.auto ?? false,
)
}
export default defineNuxtPlugin({
name: 'pinia-plugin-persistedstate',
setup({ $pinia }) {
($pinia as Pinia).use(piniaPlugin)
},
})