
Nuxt EdgeDB
Nuxt 3 と EdgeDB を簡単に統合し、最小限の設定でアプリに堅牢なデータベースレイヤーを追加します。
機能
- 🍱 簡単な統合: たった1行の設定でデータベースをセットアップできます。
- 🎩 ライブスキーマ更新: スキーマ、クエリ、マイグレーションのウォッチャーで、HMRのようなDXを体験してください。
- 🛟 型付きクエリ生成: @edgedb/generate で型付きクエリクライアントを自動生成します。
- 🍩 統合されたデータベース管理: Nuxt DevTools からデータベースを操作します。
- 🔐 柔軟な認証: カスタム認証プロバイダーをサポートし、メールまたはOAuth認証を1行で切り替えることができます。
- 🧙 初期ガイダンス: 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 [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 を介して公開されるサーバーのみのデータベースとして使用し、認証を不要にすることができます。
しかし、一部のプロジェクトでは、ユーザーがログインしてサーバー上でもIDを持つようにしたい場合があります。幸いなことに、このモジュールはそれをカバーしています。
これらの認証設定手順に進む前に、EdgeDB Auth のドキュメントをよく読むことを強くお勧めします。
Nuxt の設定で auth オプションを有効にします。
export default defineNuxtConfig({
modules: ['nuxt-edgedb-module'],
edgedb: {
auth: true
}
})
スキーマに EdgeDB 認証を設定
この例では、次のことに気づくでしょう。
global current_userは、クライアントトークンのIDにリンクされた グローバルプロパティ を定義します。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_keyallowed_redirect_urls
いくつかのプロバイダーも有効にする必要があります。例えば Email + Password から始めることができます。
required_verification を有効にした場合、EdgeDB インスタンス用の SMTP サーバーを設定する必要があります。
この機能を試すためにローカルで Mailtrap を使用する方法についての詳細は こちら を参照してください。
これらの手順は本番環境でも実行する必要があることを忘れないでください。
クライアントでの認証コンポーネントの使用
設定で認証を有効にすると、モジュールがこれらのコンポーネントをプロジェクトに注入します。
EdgeDbAuthEmailLoginEdgeDbAuthEmailVerifyEdgeDbAuthLogoutEdgeDbAuthResetPasswordEdgeDbAuthSendPasswordResetEdgeDbAuthSignupEdgeDbAuthProviders
これらのコンポーネントのソースを見ると、そのプロパティについて詳しく知ることができます。
これらはすべて、スムーズな認証フローを実現するために必要なロジックのみを公開する、スタイルなしのコンポーネントです。
<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
}
})
これにより、2つの新しいコンポーネントがアプリに注入されます。
EdgeDB は現在、以下のプロバイダーの OAuth をサポートしています。
- Apple
- Azure (Microsoft)
- GitHub
OAuth を機能させるには、Nuxt DevTools を介して EdgeDB インスタンスの UI にアクセスする必要があります。
データベースにアクセスし、「Auth Admin」タブに移動します。
プロバイダーのリストで、任意のプロバイダーを追加し、必要なキー(通常はクライアントの appid と secret)を設定できます。
プロバイダのコールバックURLをEdgeDB Auth Adminのトップに表示されているものに設定することを忘れないでください。
次に、アプリにこのような簡単な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 というクッキーを使用します。
サーバー側で、現在のユーザーに対するデータベースへのリクエストを認証したい場合は、現在のリクエストオブジェクトをコンポーザブルに渡すだけです。
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 をクッキーとして使用することもできます。
EdgeDB上に構築されたカスタム認証の素晴らしい例がたくさん詰まっている https://github.com/edgedb/edgedb-examples をご覧になることをお勧めします。
認証環境変数
# Your EdgeDB instance auth extension base URL
NUXT_EDGEDB_AUTH_BASE_URL=https://:10702/db/edgedb/ext/auth/
# Your EdgeDB instance OAuth callback URL
NUXT_EDGEDB_OAUTH_CALLBACK=https://:10702/db/edgedb/ext/auth/callback
# Your app callback page
NUXT_EDGEDB_OAUTH_REDIRECT_URL=https://:3000/auth/callback
# Your app app reset password URL (receiving the token from the forgot password email)
NUXT_EDGEDB_AUTH_RESET_PASSWORD_URL=https://:3000/auth/reset-password
# Your app email verify url (receiving the token from email verify feature)
NUXT_EDGEDB_AUTH_VERIFY_REDIRECT_URL=https://:3000/auth/verify
さらに認証を進める
EdgeDB Auth は、認証のための最小限の ID 機能しか提供しません。
認証設定で提供されたコード例では、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 は、自己ホスト型として設計されたオープンソースデータベースです。
しかし、環境変数のおかげでこのモジュールと完全に互換性のある Cloud も提供しています。
コンポーザブルで使用される 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
