I was recently working on a side project in my free time when I ran into a really weird issue with Supabase and, specifically the @nuxtjs/supabase
client library.
What I was attempting to do was to make an API call to get data to populate a profile page as soon as the user loaded the page. We had set up the backend API to require a basic auth token. The code looked something like this:
<script setup>
const client = useSupabaseAuthClient();
const session = await client.auth.getSession();
await useFetch(`${API_URL}/profile/:user_id`, {
headers: {
Authorization: `Bearer ${session.data.session.access_token}`,
},
onResponse({ request, response, options }) {
console.log(response);
//do other stuff
},
});
Rather than console logging out a 200 we got an error before we could even make a request.
TypeError: Cannot read properties of null (reading 'access_token')
Well that’s weird why would the client.auth.getSession()
not have a session?
What was weirder was just console.logging it out would also give us the same error
const client = useSupabaseAuthClient();
const session = await client.auth.getSession();
console.log(session.data.session.access_token);
What if we tried this?
const client = useSupabaseAuthClient();
const session = await client.auth.getSession();
console.log(session.data.session?.access_token);
Funny enough this actually worked. It would log out the correct access token. It was at this moment I realized what was wrong. But I wanted to confirm if my thinking was correct with one last experiment.
I decided to wrap the entire fetch call with a try/catch block
try {
const { data: fetchedProfileData, refresh } = await useFetch(
`${API_URL}/profile/:user_id`,
{
headers: {
Authorization: `Bearer ${
(
await client.auth.getSession()
).data.session.access_token
}`,
},
onResponse({ request, response, options }) {
console.log(response);
},
}
);
} catch (e) {
console.error(e);
}
So if you know the answer to this problem, it shouldn’t surprise you that the page loaded fine and the data was logged out properly. But at the same time there was also a second console.log message.
TypeError: Cannot read properties of null (reading 'access_token')
...
//Access token logged here:
How could we be getting an error that the session is null but then log the access token at the same time?
So I want to say the core problem was that for whatever reason, a large part of client.auth.getSession()
is not setup until after things are rendered in the lifecycle. And if you don’t explicitly state a lifecycle method our API call would run before the initial render. Whether that’s intentional or not I’m not exactly sure. The API call works with a try/catch block because on that initial load the API call errors out but as we continue our way along the Vue lifecycle the API call gets called again and this time around client.auth.getSession()
is set properly and we can go on our merry way.
You can use the try/catch block but the better and cleaner way to do things is to use a lifecycle hook. In this instance onMounted
or onBeforeMount
works well. Docs here
onMounted(async () => {
const session = await client.auth.getSession();
await useFetch(`${API_URL}/profile/:user_id`, {
headers: {
Authorization: `Bearer ${session.data.session.access_token}`,
},
onResponse({ request, response, options }) {
console.log(response);
//do other stuff
},
});
});
Voila, now the errors are gone and we have our data. With this you can also trigger the error if we use the onServerPrefetch
hook because we’re forcing our fetch to be called before the component instance is even rendered on the server
//This will error
onServerPrefetch(async () => {
const session = await client.auth.getSession();
const x = await useFetch(`${API_URL}/profile/:user_id`, {
headers: {
Authorization: `Bearer ${session.data.session.access_token}`,
},
onResponse({ request, response, options }) {
console.log(response);
//do other stuff
},
});
});
I hope this helps anyone else who ran into this issue with Supabase and Nuxt and hopefully someone reads this and can give a better explanation or confirm my suspicions in the comments since I’m mostly a backend developer and I only touch frontend code in my free time.