nuxt-edgedb-module
Nuxt EdgeDB
Nuxt 3とEdgeDBを簡単に統合し、最小限の設定で、堅牢なデータベース層をアプリケーションに追加します。
機能
- 🍱 容易な統合:1行の設定だけでデータベースを設定できます。
- 🎩 ライブスキーマ更新:スキーマ、クエリ、マイグレーションのウォッチャーによる、HMRのような開発者エクスペリエンスを実現します。
- 🛟 型付きクエリの生成:@edgedb/generateを使用して、型付きクエリクライアントを自動的に生成します。
- 🍩 統合データベース管理:Nuxt DevToolsからデータベースを操作できます。
- 🔐 柔軟な認証:1行でメールまたはOAuth認証を切り替え可能。カスタム認証プロバイダーもサポートしています。
- 🧙 初期ガイダンス:EdgeDB CLIの設定とプロジェクトの初期化をガイドします。
クイックセットアップ
- プロジェクトに
nuxt-edgedb-module
依存関係を追加します。
npx nuxi@latest module add edgedb
nuxt.config.ts
のmodules
セクションにnuxt-edgedb-module
を追加します。
export default defineNuxtConfig({
modules: [
'nuxt-edgedb-module'
]
})
- プロジェクトルートで
npx nuxt-edgedb-module
を実行して、CLIセットアップウィザードを実行します。
npx nuxt-edgedb-module
これで完了です!Nuxtプロジェクトにデータベースが追加されました。✨
マシンにEdgeDBがまだインストールされていない場合は、インストールウィザードがインストールを支援します。
サンプルプロジェクト
サンプルプロジェクトを実行するには、このリポジトリをクローンしてプレイグラウンドを実行する必要があります。
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/
コンテキストで使用可能なすべてのコンポーザブルを自動的にインポートします。
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>
認証
server/api
エンドポイントとクライアント側の$fetch
を介して公開されるサーバー専用データベースとしてEdgeDBを使用することで、認証の必要性を回避できます。
しかし、一部のプロジェクトでは、ユーザーにログインさせてサーバー上にもアイデンティティを持たせたい場合があります。幸いにも、このモジュールはそのニーズに対応できます。
これらの認証インストール手順を実行する前に、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 create
とedgedb migrate
を実行できます。
サーバーにEdgeDB認証を設定します。
EdgeDBサーバーで認証プロバイダーを有効にする必要があります。
これは、DevToolsを介してEdgeDBタブで行うことができます。
データベースを参照してAuth Admin
に移動し、以下を指定します。
auth_signing_key
allowed_redirect_urls
いくつかのプロバイダーも有効にする必要があります。例えばEmail + Password
から始められます。
required_verification
を有効にした場合、EdgeDBインスタンスにSMTPサーバーを設定する必要があります。
この機能を試すためにローカルでMailtrapを使用する方法については、こちらを参照してください。
これらの手順は、本番環境でも実行する必要があることに注意してください。
クライアントで認証コンポーネントを使用します。
設定で認証を有効にすると、モジュールによってこれらのコンポーネントがプロジェクトに注入されます。
EdgeDbAuthEmailLogin
EdgeDbAuthEmailVerify
EdgeDbAuthLogout
EdgeDbAuthResetPassword
EdgeDbAuthSendPasswordReset
EdgeDbAuthSignup
EdgeDbAuthProviders
これらのコンポーネントのソースコードを参照して、プロパティの詳細を確認できます。
これらはすべて、スムーズな認証フローを実現するために必要なロジックのみを公開する、スタイルのないコンポーネントです。
<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
}
})
これにより、アプリケーションに2つの新しいコンポーネントが注入されます。
EdgeDBは現在、以下のプロバイダーでOAuthをサポートしています。
- Apple
- Azure (Microsoft)
- GitHub
OAuthを動作させるには、Nuxt DevToolsを介してEdgeDBインスタンスのUIにアクセスする必要があります。
データベースを参照し、「Auth Admin」タブに移動します。
プロバイダーの一覧で、必要なプロバイダーを追加し、必要なキー(通常はクライアントappid
とsecret
)を設定できます。
プロバイダーのコールバック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 }
})
その他の認証ソリューション
EdgeDB認証は優れたソリューションですが、最終的にはアプリケーションでより多くの機能が必要になる場合があります。
EdgeDBはデータベースとしても使用できることを忘れないでください。独自の認証を構築したり、以下の既存のソリューションを使用したりできます。
- Sidebase Nuxt Auth
- Nuxt Auth (準備完了次第)
- Nuxt Auth Utils
- 独自のインプリメンテーション
両方を使用し、独自の認証プロバイダーからIdentityオブジェクトを作成し、edgedb-auth-token
をCookieとして使用することもできます。
カスタム認証の優れた例が多数掲載されているhttps://github.com/edgedb/edgedb-examplesを参照することをお勧めします。
認証環境変数
# 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認証は、認証のための最小限のアイデンティティ機能のみを提供します。
認証設定のために提供されているコード例では、Identity
インターフェースと共にUser
型が使用されています。
登録時に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=
環境変数を使用する場合は、すべてを指定する必要があります。そうでない場合、クライアントはデフォルト値に戻ります。
Q&A
データベースクライアントはユーザーランドで公開されますか?
いいえ、useEdgeDb
と useEdgeDbQueries
は、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 がインストールされていることを確認してください。
- CLIスクリプトに
edgedb migrate --quiet
を追加します。
生成されたファイルをバージョン管理する必要がありますか?
いいえ、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
によって取得されているようですが、パッケージは非アクティブのようです。
彼を知っている人がいれば、連絡を取りたいと思います!
貢献
この統合のために構築できる優れた機能はまだたくさんあります。
プルリクエストを喜んで受け付けてレビューします。
開発
# 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