Nuxt Nation 大会即将到来。加入我们,时间为 11 月 12 日至 13 日。

edgedb
nuxt-edgedb-module

轻松地将 Nuxt 3 与 EdgeDB 集成,通过最少的配置为您的应用程序添加强大的数据库层。

nuxt-edgedb-module

Nuxt EdgeDB

npm versionnpm downloadsLicenseNuxt

轻松地将 Nuxt 3EdgeDB 集成,通过最少的配置为您的应用程序添加强大的数据库层。

功能

  • 🍱 轻松集成:只需一行配置即可设置数据库。
  • 🎩 实时模式更新:通过观察模式查询迁移体验类似 HMR 的 DX
  • 🛟 类型化查询生成:使用 @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,安装向导将帮助您安装它。

示例项目

如果您想运行示例项目,您必须克隆此存储库并运行游乐场。

由于 EdgeDB 无法在像 Stackblitz 或 CodeSandbox 这样的 Web 容器环境中运行。

git clone [email protected]: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/ 上下文中可用的所有组合式函数。

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

useEdgeDbQueries 公开来自 dbschema/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 直接导入 查询 并向它们传递来自 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 将生成的 查询构建器 直接公开到您的 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
  }
})

在您的模式中设置 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;
  }
}

您可以在服务器运行时编辑此模式,并接受提示的消息以自动迁移。

如果您在服务器关闭时进行这些编辑,您可以运行 edgedb migration createedgedb migrate

在您的服务器上设置 EdgeDB 身份验证

您需要在您的 EdgeDB 服务器上启用身份验证提供程序。

这可以通过 DevTools 中的 EdgeDB 选项卡完成。

浏览您的数据库到 Auth Admin 并指定

  • auth_signing_key
  • allowed_redirect_urls

您还必须启用一些提供程序。例如,您可以从 电子邮件 + 密码 开始。

如果您启用了 required_verification,则需要为您的 EdgeDB 实例配置一个 SMTP 服务器。

您可以在此处找到有关如何在本地使用 Mailtrap 来尝试此功能的更多说明 此处

不要忘记这些步骤也必须在您的生产环境中执行。

在客户端上使用身份验证组件

由于您在配置中启用了身份验证,因此该模块将这些组件注入到您的项目中

您可以查看这些组件的源代码以了解有关其道具的更多信息。

它们都是未设置样式的组件,仅公开实现流畅身份验证流程所需的逻辑。

<template>
  <EdgeDbAuthEmailLogin
    v-slot="{ email, updateEmail, password, updatePassword, submit, loading }"
    redirect-to="/"
  >
    <div>
      <input
        type="email"
        :value="email"
        placeholder="[email protected]"
        @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 组合式函数。

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

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

您可以查看 useEdgeDbIdentity 以获取更多详细信息。

服务器端用法

身份验证过程确实使用名为 edgedb-auth-token 的 Cookie。

在服务器端,如果您想为当前用户对数据库请求进行身份验证,您只需将当前请求对象传递给组合式函数

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 身份验证是一个很好的解决方案,但最终您的应用程序以后可能需要更多功能。

不要忘记 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 身份验证仅提供身份验证的基本最小标识功能。

在为身份验证设置提供的代码示例中,User 类型与 Identity 接口一起使用。

如果您想在注册时使用其他属性填充 User,则必须自己实现此行为。

如果您想从 OAuth 提供程序解析数据,您可以使用 Nitro 插件中的名为 edgedb:auth:callback 的 Nitro 钩子。

// 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 是一个开源数据库,旨在进行自托管。

但是,他们还提供了一个 ,由于环境变量,它与该模块完全兼容。

如果您想自定义组合式函数使用的 DSN,您可以使用模块提供的环境变量

NUXT_EDGEDB_HOST=
NUXT_EDGEDB_PORT=
NUXT_EDGEDB_USER=
NUXT_EDGEDB_PASS=
NUXT_EDGEDB_DATABASE=

如果您想使用 env 变量,则必须指定所有变量,否则客户端将回退到默认值。

问答

我的数据库客户端是否会在用户端公开?

不会,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>

您还可以选择性地将查询构建器导入到客户端。

我认为这对于超级管理员/内部仪表板很有用,但在安全访问方面请自行承担风险。

<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 选项,则必须相应地更改这些路径。

数据库模式的 HMR 是否真的安全?

这取决于您想何时使用它。

建议在您休闲开发项目时保持 watchPrompt 启用。

这将阻止运行任何不需要的迁移,并且只会在您向模式添加新内容时提示。

如果您想加快速度并且知道自己在做什么,可以将 watchPrompt 设置为 false,并在模式发生任何更改时利用自动迁移创建和应用功能。

如果您不希望使用这些功能中的任何一个,只需将 watch 设置为 false,即可确保对开发数据库所做的更改安全。

数据库上的 HMR 在生产环境中显然 **没有任何** 影响。

为什么名称不是 nuxt-edgedb

因为该名称已被 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