• בלוג
  • תבנית פרויקט: nuxt, drizzle, auth0

תבנית פרויקט: nuxt, drizzle, auth0

27/01/2025

אתמול פירסמתי פה תבנית לפרויקט React שמשתמש ב Next.js ומכיל קומפוננטות צד שרת, משיכת מידע מבסיס נתונים וניהול משתמשים עם auth0. היום נראה את החלק השני של הפוסט ונבנה את אותה תבנית עבור nuxt ליישומי vue. הקוד כאן:

https://github.com/ynonp/nuxt-drizzle-auth0-demo

בואו נראה מה יש בריפו.

1. ניהול משתמשים

ניהול המשתמשים במערכת מתחבר ל auth0 כדי שאנחנו לא נצטרך להתאמץ, בדיוק כמו בדוגמת ה next. זה אומר שבצד שלנו אנחנו צריכים לטפל באירוע של משתמש שהתחבר או התנתק, ונוכל להשתמש בפונקציות של auth0 כדי להבין אם יש משתמש מחובר.

הקוד מתחיל בקובץ server/routes/auth/auth0.get.ts:

export default defineOAuthAuth0EventHandler({
  async onSuccess(event, { user, tokens }) {
    await setUserSession(event, {
      user: {
        id: user.sub
      }
    })

    return sendRedirect(event, '/hello')
  },
  // Optional, will return a json error and 401 status code by default
  onError(event, error) {
    console.log(`error in login`);
    return sendRedirect(event, '/')
  },
})

זה הקוד שמטפל באירועים שמגיעים מ auth0 כשמשתמש מתחבר למערכת. בצד של nuxt אני לוקח את user.sub שזה מזהה המשתמש ושותל אותו ב session כדי שנוכל לגשת אליו מהקומפוננטות ומנתיבי צד שרת.

הקומפוננטה הבאה בקובץ pages/hello.vue כבר כוללת קוד שמאפשר חיבור למערכת:

<script setup>
import People from '../components/People.vue';

const { loggedIn, user, session, fetch, clear } = useUserSession()
</script>

<template>
  <div v-if="loggedIn">
    <h1>Welcome {{ user.id }}!</h1>
    <p>Logged in since {{ session.loggedInAt }}</p>
    <button @click="clear">Logout</button>
    <People />
  </div>
  <div v-else>
    <h1>Not logged in</h1>
    <a href="/auth/auth0">Login with Auth0</a>
  </div>
</template>

בשביל להתחבר למערכת צריך רק לעבור לנתיב /auth/auth0 ובשביל להתנתק צריך להפעיל את הפונקציה clear שמנקה את ה Session. כל הקוד הזה משתמש במודול auth-utils של nuxt.

בשביל החיבור ל Auth0 יש להגדיר את משתני הסביבה שלהם בקובץ ה .env. אלה המפתחות שעליכם להגדיר בקובץ כדי שהפרויקט יעבוד:

NUXT_SESSION_PASSWORD=
NUXT_OAUTH_AUTH0_CLIENT_ID=
NUXT_OAUTH_AUTH0_CLIENT_SECRET=
NUXT_OAUTH_AUTH0_DOMAIN=
DATABASE_URL=

2. חיבור לבסיס נתונים

החיבור לדריזל משתמש בדיוק באותו קוד שהראיתי אתמול לגבי next. בצד של nuxt העברת המידע מבסיס הנתונים לפרונט אנד קצת שונה:

  1. יש להגדיר נתיב צד שרת שימשוך את הנתונים מבסיס הנתונים.

  2. יש להפעיל את הפונקציה useAsyncData של nuxt מתוך הקומפוננטה וממנה למשוך את המידע מהנתיב שיצרנו.

בשביל הסעיף הראשון אני יוצר קובץ server/routes/api/people.get.ts עם התוכן הבא:

import { usersTable } from '@/db/schema';
import { db } from "@/db/drizzle";

export default eventHandler(async (event) => {
  try {
    const res = await requireUserSession(event)
    const users = await db.select().from(usersTable);
    return users;
  } catch (err) {
    return [];
  }
})

אם המשתמש מחובר הוא יקבל את רשימת המשתמשים מבסיס הנתונים ובשביל המשחק אם המשתמש לא מחובר נחזיר לו רשימה ריקה.

הקומפוננטה שמשתמשת בנתיב זה שמורה בקובץ components/People.vue וזה הקוד שלה:

<script setup lang="ts">
const { data, status, error, refresh } = await useAsyncData(
  'people',
  () => $fetch('/api/people', {
     credentials: 'include',
     headers: useRequestHeaders(['cookie']),
  })
)
</script>

<template>
  <h1>People. Status = {{ status }}</h1>
  <ul>
    <li v-for="person in data">{{ person.email }}</li>
  </ul>
</template>

<style lang="css" scoped>
</style>

השימוש ב fetch מתוך useAsyncData נראה כמו בקשת רשת אבל למעשה זו בקשה מקומית כי הקוד רץ בצד השרת ב SSR. כשהקוד יישלח לדפדפן הוא יכלול כבר את התשובה (כלומר את רשימת המשתמשים). נשים לב שאני חייב להעביר את ה cookie מהדפדפן בשביל לשמור על ה Session. שאר הקוד די פשוט הוא לוקח את רשימת המשתמשים מהנתיב שהגדרנו ומציג את כתובות המייל שלהם על המסך.

מאחר ו vercel תומך ב nuxt אין לנו בעיה לעשות deploy בלחיצה אחת ולקבל את כל הפינוקים של vercel. בסיס הנתונים הוא עדיין neon וגם הוא שמור בענן.