透過 100 多個技巧的集合來學習 Nuxt!

edgedb
nuxt-edgedb-module

輕鬆將 Nuxt 3 與 EdgeDB 整合,以最少的配置為您的應用程式添加強大的資料庫層。

nuxt-edgedb-module

Nuxt EdgeDB

npm versionnpm downloadsLicenseNuxt

輕鬆將 Nuxt 3EdgeDB 整合,以最少的配置為您的應用程式添加強大的資料庫層。

功能特色

  • 🍱 輕鬆整合:只需一行配置即可設定資料庫。
  • 🎩 即時 Schema 更新:體驗 HMR 般的 DX,監看 schemaqueriesmigrations
  • 🛟 類型化查詢生成:使用 @edgedb/generate 自動生成類型化查詢客戶端。
  • 🍩 整合式資料庫管理:從 Nuxt DevTools 管理您的資料庫。
  • 🔐 彈性驗證:一鍵切換 電子郵件OAuth 驗證,並支援自訂驗證供應商。
  • 🧙 初始指南:引導您完成 EdgeDB CLI 設定和 專案初始化

快速設定

  1. nuxt-edgedb-module 依賴項添加到您的專案
npx nuxi@latest module add edgedb
  1. nuxt-edgedb-module 添加到 nuxt.config.tsmodules 區段
export default defineNuxtConfig({
  modules: [
    'nuxt-edgedb-module'
  ]
})
  1. 在您的專案根目錄中執行 npx nuxt-edgedb-module 以執行 CLI 設定精靈。
npx nuxt-edgedb-module

就是這樣!您的 Nuxt 專案現在擁有資料庫了。 ✨

如果您尚未在您的機器上安裝 EdgeDB,安裝精靈將協助您安裝。

範例專案

如果您想執行範例專案,您必須複製此儲存庫並執行 playground。

由於 EdgeDB 無法在 Stackblitz 或 CodeSandbox 等 Web 容器環境中執行。

git clone git@github.com:Tahul/nuxt-edgedb.git
cd nuxt-edgedb
pnpm install
pnpm stub
cd playground
edgedb project init
npx nuxt-edgedb-module
pnpm run dev

模組選項

您可以從您的 nuxt.config.ts 檔案配置模組的任何行為

export default defineNuxtConfig({
  modules: ['nuxt-edgedb-module'],
  edgeDb: {
    // Devtools integrations
    devtools: true,
    // Completely toggle watchers feature
    watch: true,
    // Enable or disable prompts on watch events
    watchPrompt: true,
    // Generate target for your queries and query builder
    generateTarget: 'ts',
    // dbschema/ dir
    dbschemaDir: 'dbschema',
    // Individual queries dir (useEdgeDbQueries composable)
    queriesDir: 'queries',
    // Toggle CLI install wizard
    installCli: true,
    // Toggles composables
    composables: true,
    // Toggles auto-injection on auth credentials
    injectDbCredentials: true,
    // Enables authentication integration
    auth: false,
    // Enables oauth integration
    oauth: false,
  }
})

伺服器端用法

此模組自動導入 Nuxt 應用程式 server/ 環境中所有可用的 composable。

useEdgeDb

useEdgeDb 使用您的 Nuxt 環境配置,從 edgedb 導入公開原始客戶端。

// server/api/blogpost/[id].ts
import { defineEventHandler, getRouterParams } from 'h3'

export default defineEventHandler(async (req) => {
  const params = getRouterParams(req)
  const id = params.id
  const client = useEdgeDb()

  const blogpost = await client.querySingle(`
    select BlogPost {
      title,
      description
    } filter .id = <uuid>$id
  `, {
    id: id
  });

  return blogpost
})

useEdgeDbQueries

useEdgeDbQueriesdbschema/queries.ts 公開您所有的查詢。

您不必傳遞客戶端給它們。它們將使用由 useEdgeDb 生成的客戶端,該客戶端的作用域限定於當前請求。

// queries/getBlogPost.edgeql
select BlogPost {
  title,
  description
} filter .id = <uuid>$blogpost_id
// server/api/blogpost/[id].ts
import { defineEventHandler, getRouterParams } from 'h3'

export default defineEventHandler(async (req) => {
  const params = getRouterParams(req)
  const id = params.id
  const { getBlogpPost } = useEdgeDbQueries()
  const blogPost = await getBlogpost({ blogpost_id: id })

  return blogpost
})

您仍然可以從 #edgedb/queries 直接導入 queries,並將來自 useEdgeDb() 的客戶端傳遞給它們。

// server/api/blogpost/[id].ts
import { getBlogPost } from '#edgedb/queries'
import { defineEventHandler, getRouterParams } from 'h3'

export default defineEventHandler(async (req) => {
  const params = getRouterParams(req)
  const id = params.id
  const client = useEdgeDb()
  const blogPost = await getBlogpost(client, { blogpost_id: id })

  return blogpost
})

useEdgeDbQueryBuilder

useEdgeDbQueryBuilder 直接將生成的 query builder 公開到您的 server/ 環境。

// server/api/blogpost/[id].ts
import { defineEventHandler, getRouterParams } from 'h3'

export default defineEventHandler(async (req) => {
  const params = getRouterParams(req)
  const id = params.id
  const client = useEdgeDb()
  const e = useEdgeDbQueryBuilder()

  const blogPostQuery = e.select(
    e.BlogPost,
    (blogPost) => ({
      id: true,
      title: true,
      description: true,
      filter_single: { id }
    })
  )

  const blogPost = await blogPostQuery.run(client)

  return blogpost
})

類型定義

所有由 EdgeDB 生成的介面都可以透過 #edgedb/interfaces 導入來使用。

<script setup lang="ts">
import type { BlogPost } from '#edgedb/interfaces'

defineProps<{ blogPost: BlogPost }>()
</script>

身份驗證

您可以將 EdgeDB 用作僅限伺服器端的資料庫,透過 server/api 端點和客戶端上的 $fetch 公開,從而避免驗證的需求。

但在某些專案中,您可能希望使用者登入,以便他們在伺服器端也具有身分。幸運的是,此模組已涵蓋了這部分。

在完成這些驗證安裝步驟之前,我們強烈建議您閱讀 EdgeDB 驗證 文件。

在您的 Nuxt 配置中啟用 auth 選項

export default defineNuxtConfig({
  modules: ['nuxt-edgedb-module'],
  edgedb: {
    auth: true
  }
})

在您的 schema 中設定 EdgeDB 驗證

在此範例中,您可以注意到

  • global current_user 定義了一個連結到客戶端令牌身分的 全域屬性
  • type User 是您的使用者模型,您可以自由更改它,稍後可以透過遷移來完成。
  • access policy author_has_full_access & using (.author ?= global current_user); 定義了使用者僅能存取他們自己的 BlogPost 的策略。
// dbschema/default.esdl
using extension auth;

module default {
  global current_user := (
    assert_single((
      select User { id, name }
      filter .identity = global ext::auth::ClientTokenIdentity
    ))
  );

  type User {
    required name: str;
    required identity: ext::auth::Identity;
  }

  type BlogPost {
    property content: str {
      default := 'My blog post content.';
    };
    property title: str {
      default := 'My blog post';
    };
    required author: User;

    access policy author_has_full_access
      allow all
      using (.author ?= global current_user);

    access policy others_read_only
      allow select;
  }
}

您可以在伺服器運作時編輯此 schema,並接受提示訊息以自動遷移。

如果您在伺服器關閉時進行這些編輯,您可以執行 edgedb migration createedgedb migrate

在您的伺服器端設定 EdgeDB 驗證

您將需要在您的 EdgeDB 伺服器上啟用驗證供應商。

這可以在 DevTools 中透過 EdgeDB 標籤完成。

瀏覽您的資料庫到 Auth Admin 並指定

  • auth_signing_key
  • allowed_redirect_urls

您還必須啟用一些供應商。您可以從 Email + Password 開始。

如果您啟用 required_verification,您將需要為您的 EdgeDB 實例配置 SMTP 伺服器。

您可以在 Mailtrap 上找到有關如何在本地嘗試此功能的更多說明 這裡

不要忘記這些步驟也必須在您的生產環境中執行。

在客戶端使用驗證組件

當您在配置中啟用驗證時,模組會在您的專案中注入這些組件

您可以查看這些組件的原始碼,以了解更多關於它們的 props。

它們都是無樣式的組件,僅公開實現流暢驗證流程所需的邏輯。

<template>
  <EdgeDbAuthEmailLogin
    v-slot="{ email, updateEmail, password, updatePassword, submit, loading }"
    redirect-to="/"
  >
    <div>
      <input
        type="email"
        :value="email"
        placeholder="your@email.com"
        @change="(e) => updateEmail(e.target.value)"
      >
      <input
        type="password"
        :value="password"
        placeholder="password"
        @change="(e) => updatePassword(e.target.value)"
      >
      <button
        type="button"
        @click="(e) => !loading && submit()"
      >
        {{ loading ? 'Loading' : 'Login' }}
      </button>
    </div>
  </EdgeDbAuthEmailLogin>
</template>

當然,您可以完全在本地重寫任何這些組件,以實現您自己的驗證流程。

OAuth

如果您想使用 OAuth,您必須在您的 nuxt.config 中啟用它

export default defineNuxtConfig({
  edgeDb: {
    oauth: true
  }
})

這將為您的應用程式注入兩個新的組件

EdgeDB 目前支援以下供應商的 OAuth

  • Apple
  • Azure (Microsoft)
  • GitHub
  • Google

為了使 OAuth 運作,您必須透過 Nuxt DevTools 訪問您的 EdgeDB 實例 UI。

瀏覽到您的資料庫並訪問 "Auth Admin" 標籤。

在您的供應商列表中,您可以添加任何您想要的供應商,並配置必要的金鑰(通常是客戶端 appidsecret)。

不要忘記將您的供應商的回調 URL 設定為在您的 EdgeDB Auth Admin 頂部列出的 URL。

然後,您可以在您的應用程式中創建一個簡單的 OAuth 按鈕,如下所示

<template>
  <!-- Gives access to all available auth providers -->
  <EdgeDbAuthProviders v-slot="{ oAuthProviders: providers }">
    <!-- Create a OAuth button behavior from a provider name -->
    <EdgeDbOAuthButton
      v-for="provider of providers"
      :key="provider.name"
      v-slot="{ redirect }"
      :provider="provider.name"
    >
      <!-- Call `redirect` from the OAuthButton -->
      <button @click="() => redirect()">
        {{ provider.display_name }}
      </button>
    </EdgeDbOAuthButton>
  </EdgeDbAuthProviders>
</template>

您還需要一個回調頁面,可以使用 EdgeDbAuthCallback

<template>
  <EdgeDbOAuthCallback
    v-slot="{ loading }"
    redirect-to="/"
  >
    <div>
      <h2>OAuth callback</h2>
      <p v-if="loading">
        Loading...
      </p>
      </UCard>
    </div>
  </EdgeDbOAuthCallback>
</template>

太棒了,對吧?!只需幾行程式碼,我們就為我們的應用程式添加了基本的身份驗證。

客戶端用法

現在身份驗證已實作,您也可以在您的 Nuxt 應用程式中使用 useEdgeDbIdentity composable。

<script setup lang="ts">
const { isLoggedIn } = useEdgeDbIdentity()
</script>

<template>
  <div>
    <LoginButton v-if="isLoggedIn" />
    <LogoutButton v-else />
  </div>
</template>

您可以查看 useEdgeDbIdentity 以獲取更多詳細資訊。

伺服器端用法

身份驗證過程確實使用了名為 edgedb-auth-token 的 cookie。

在伺服器端,如果您想驗證您對當前使用者的資料庫請求,您只需將當前請求物件傳遞給 composable

export default defineEventHandler(async (req) => {
  // Will throw an error, as you cannot delete a BlogPost without being the author
  const { deleteBlogPost } = useEdgeDbQueries()
  await deleteBlogPost({ blogpost_id: id })

  // Success
  const { deleteBlogPost: deleteBlogPostAuthenticated } = useEdgeDbQueries(req)
  await deleteBlogPostAuthenticated({ blogpost_id: id })

  return { id }
})

其他驗證解決方案

EdgeDDB Auth 是一個很棒的解決方案,但最終您的應用程式可能需要在以後有更多功能。

不要忘記 EdgeDB 也可以用作資料庫。您可以建立自己的驗證或使用現有的解決方案,例如

您也可以同時使用兩者,並從您自己的身份驗證供應商創建 Identity 物件,並使用 edgedb-auth-token 作為您的 cookie。

我建議查看 https://github.com/edgedb/edgedb-examples,其中充滿了基於 EdgeDB 構建的自訂驗證的絕佳範例。

身份驗證環境變數

# Your EdgeDB instance auth extension base URL
NUXT_EDGEDB_AUTH_BASE_URL=https://127.0.0.1:10702/db/edgedb/ext/auth/
# Your EdgeDB instance OAuth callback URL
NUXT_EDGEDB_OAUTH_CALLBACK=https://127.0.0.1:10702/db/edgedb/ext/auth/callback
# Your app callback page
NUXT_EDGEDB_OAUTH_REDIRECT_URL=https://127.0.0.1:3000/auth/callback
# Your app app reset password URL (receiving the token from the forgot password email)
NUXT_EDGEDB_AUTH_RESET_PASSWORD_URL=https://127.0.0.1:3000/auth/reset-password
# Your app email verify url (receiving the token from email verify feature)
NUXT_EDGEDB_AUTH_VERIFY_REDIRECT_URL=https://127.0.0.1:3000/auth/verify

深入了解身份驗證

EdgeDB Auth 僅為身份驗證提供最基本的身分功能。

在為身份驗證設定提供的程式碼範例中,User 類型與 Identity 介面一起使用。

如果您想在註冊時使用其他屬性填充 User,您將必須自行實作此行為。

如果您想從 OAuth 供應商解析資料,您可以使用來自 Nitro 外掛程式的 Nitro hook,稱為 edgedb:auth:callback

// server/plugins/auth.ts

export default defineNitroPlugin((app) => {
  app.hooks.hook('edgedb:auth:callback', (data) => {
    const {
      code,
      verifier,
      codeExchangeUrl,
      codeExchangeResponseData,
    } = data

    // codeExchangeResponseData contains the OAuth token from the provider.
    // ... implement your own authentication logic.
  })
})

生產環境

如果您想擺脫開發並將您的資料庫部署到生產環境,您必須遵循 EdgeDB 指南

EdgeDB 是一個設計為自託管的開源資料庫。

但是,他們也提供 Cloud,由於環境變數,它與此模組完全相容。

如果您想自訂 composable 使用的 DSN,您可以使用模組提供的環境變數

NUXT_EDGEDB_HOST=
NUXT_EDGEDB_PORT=
NUXT_EDGEDB_USER=
NUXT_EDGEDB_PASS=
NUXT_EDGEDB_DATABASE=

如果您想使用環境變數,您必須指定所有這些變數,否則客戶端將回退到預設值。

問與答

我的資料庫客戶端會在使用者端公開嗎?

不會,useEdgeDbuseEdgeDbQueries 僅在 Nuxt 的 server/ 環境中可用。

作為一個選擇加入功能,您可以在客戶端從 @dbschema/queries 導入查詢。

您將需要使用來自 createClient() 的客戶端為這些查詢提供服務。

<script setup lang="ts">
import { createClient } from 'edgedb'
import { getUser } from '@dbschema/queries'

const client = createClient()
const user = await getUser(client, 42)
</script>

您也可以作為一個選擇加入功能,將 query builder 導入到客戶端。

我猜這對於超級管理員/內部儀表板可能很有用,但在安全存取方面,請自行承擔風險使用它。

<script setup lang="ts">
import e, { type $infer } from '#edgedb/builder'

const query = e.select(e.Movie, () => ({ id: true, title: true }))
type result = $infer<typeof query>
//   ^ { id: string; title: string }[]
</script>

請小心這些導入,因為如果您導入錯誤的查詢,您最終可能會讓客戶端可以使用寫入操作,從而可能損壞您的資料庫。

我如何在生產環境中運行我的遷移?

  • 在您的生產環境中複製您的 Nuxt 專案
  • 確保您在伺服器上安裝了 EdgeDB CLI
  • edgedb migrate --quiet 添加到您的 CLI 腳本

我應該對生成的檔案進行版本控制嗎?

不,由於它們是使用您的 Nuxt 客戶端生成的,您應該將它們添加到您的 .gitignore

**/*.edgeql.ts
dbschema/queries.*
dbschema/query-builder
dbschema/interfaces.ts
queries/*.query.ts

如果您更改 **Dir 選項,您必須相應地更改這些路徑。

我的資料庫 schema 的 HMR 真的安全嗎?

嗯,這取決於您想何時使用它。

我建議在您隨意開發專案時保持 watchPrompt 啟用狀態。

這將防止運行任何不必要的遷移,並且僅在您向 schema 添加新內容時提示。

如果您想快速行動並知道自己在做什麼,您可以將 watchPrompt 設定為 false,並從 schema 的任何更改中獲益於自動遷移創建和應用。

如果您不想要任何這些功能,只需將 watch 設定為 false,並對應用於您的開發資料庫的更改感到安全。

您的資料庫上的 HMR 顯然在生產環境中沒有效果。

為什麼名稱不是 nuxt-edgedb

因為該 handle 已在 NPM 上被佔用。

它似乎被 ohmree 佔用,但該套件似乎處於非活動狀態。

如果有人碰巧認識他,我很樂意與他聯繫!

貢獻

此整合仍有許多很棒的功能可以構建。

我將很樂意接收和審查任何 Pull Request。

開發

# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release

贊助商

thecompaniesapi.com