nuxt-modules / supabase

Supabase module for Nuxt.
https://supabase.nuxtjs.org
MIT License
677 stars 123 forks source link

How to "augment" the Supabase user with data from another table (ie: profile) #50

Closed stursby closed 2 years ago

stursby commented 2 years ago

First off, just wanted to say thank you for this module as it's been amazing to work with!!

So I was watching some videos from a Next.js/React & Supabse tutorial and on this particular lesson the author is "augmenting" the default Supabase user data with data from a custom table (in this case, "profile").

He's using React Context & Providers along with some useEffect hooks to add custom profile fields to the shared "user" object.

So, my question is... what's the best way to handle something like this in Nuxt / with this module??

Any thoughts/ideas would be much appreciated!!

amrnn90 commented 2 years ago

This is how I do it, please note the following about using this approach:

If there was a logged in user then the route won't be loaded until the profile is also loaded (which is probably what you want), this way you won't have to do manual checks to see whether the profile is null. However, if this is not what you want then you can simply pass a lazy option to the useAsyncData call below.

(you might have to make some adjustments or corrections to this code)

composables/useAuth.ts

export async function useInitAuth() {
  const user = useSupabaseUser();
  const client = useSupabaseClient();

  const { data: profile } = await useAsyncData(
    "user_profile",
    async () => {
      if (!user.value) return null;
      return await client.from("profiles")
        .select().eq("id", user.value.id).single()
    },
    { watch: [() => user.value?.id] }
  );

  const userWithProfile = computed(() => {
    if (!user.value || !profile.value) return null;
    return { ...user.value, profile: { ...profile.value } };
  });

  async function login() {
    return await client.auth.signIn(
      { provider: "github" }
    );
  }

  async function logout() {
    await client.auth.signOut();
    navigateTo("/");
  }

  return { user: userWithProfile, login, logout };
}

export function provideAuth(authData: Awaited<ReturnType<typeof useInitAuth>>) {
  provide("auth", authData);
}

export function useAuth() {
  return inject<Awaited<ReturnType<typeof useInitAuth>>>("auth");
}

components/AuthProvider.vue

<script setup lang="ts">
import { provideAuth, useInitAuth } from "~~/composables/useAuth";

provideAuth(await useInitAuth());
</script>

<template>
  <slot />
</template>

app.vue

<template>
  <AuthProvider>
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </AuthProvider>
</template>

And finally you can use it normally in your pages like this:

<script setup lang="ts">
const { user } = useAuth();
</script>

<template>
  <div>
    <pre>{{ user.profile }}</pre>
  </div>
</template>
stursby commented 2 years ago

@amrnn90 Awesome! I will try this out. Thank you!!

nddr commented 2 years ago

Thank you! @amrnn90

CodyBontecou commented 11 months ago

Hmm, this profile.value is always null in my case. This requires me to refresh the page before the profile is there.