nuxt について
nuxtはAPIエミュレーターをNuxtアプリに組み込み、同一オリジンのルートで提供するアダプターです。プレビュー環境や統合テスト向けに、複数エミュレーター、任意の状態永続化、レスポンス内のパス書き換えに対応します。
主な機能
- 01Nuxtサーバールートにエミュレーターをマウント
- 02同一オリジンのパスで複数サービスを提供
- 03任意の状態永続化に対応
- 04レスポンス内のサービスパスを置換
- 05カスタムエミュレーター定義を受け入れ
活用シーン
- 01プレビュー環境でOAuthコールバックをテスト
- 02Nuxtアプリ内でAPIエミュレーターを実行
- 03再起動後もエミュレーター状態を保持
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vercel-labs/ emulate --skill nuxtインストール後、Claude Code で/nuxtを入力して読み込まれたか確認します。
詳細
Nuxt 統合
@emulators/adapter-nuxt パッケージは、エミュレーターを Nuxt アプリに直接組み込み、同じオリジンで実行します。デプロイのたびに OAuth コールバック URL が変わるプレビュー環境で役立ちます。
インストール
npm install @emulators/adapter-nuxt @emulators/github @emulators/google
必要なエミュレーターのみをインストールしてください。各 @emulators/* パッケージは個別に公開されるため、サーバーバンドルを小さく保てます。
サーバールート
エミュレーターへのトラフィックを処理する名前付きキャッチオールルートを作成します。
// server/routes/emulate/[...path].ts
import { createEmulateHandler } from '@emulators/adapter-nuxt'
import * as github from '@emulators/github'
import * as google from '@emulators/google'
export default defineEventHandler(createEmulateHandler({
services: {
github: {
emulator: github,
seed: {
users: [{ login: 'octocat', name: 'The Octocat' }],
repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],
},
},
google: {
emulator: google,
seed: {
users: [{ email: '[email protected]', name: 'Test User' }],
},
},
},
}))
次のルートが作成されます。
/emulate/github/**は GitHub エミュレーターを処理します/emulate/google/**は Google エミュレーターを処理します
GitHub のインストールトークンのメタデータは、サーバー側の /emulate/github/_emulate/installation-tokens で利用できます。
Nuxt 設定
エミュレーターの UI ページでは、バンドルされたフォントを使用します。Nuxt 設定をラップして、Nitro がコアパッケージのアセットを本番ビルドにトレースするようにします。
// nuxt.config.ts
import { withEmulate } from '@emulators/adapter-nuxt'
export default defineNuxtConfig(withEmulate({
// your normal Nuxt config
}))
OAuth 設定
OAuth プロバイダーが同じオリジン上のエミュレーターパスを参照するように設定します。
const baseUrl = process.env.NUXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'
export const githubOAuth = {
clientId: 'any-value',
clientSecret: 'any-value',
authorizationUrl: `${baseUrl}/emulate/github/login/oauth/authorize`,
tokenUrl: `${baseUrl}/emulate/github/login/oauth/access_token`,
userInfoUrl: `${baseUrl}/emulate/github/user`,
}
oauth_apps をシードする必要はありません。何も設定されていない場合、エミュレーターは client_id、client_secret、redirect_uri の検証をスキップします。
永続化
デフォルトでは、エミュレーターの状態はメモリ内に保持され、コールドスタートのたびにリセットされます。再起動後も状態を保持するには、persistence アダプターを渡します。
Nitro Storage
import { createEmulateHandler } from '@emulators/adapter-nuxt'
import * as github from '@emulators/github'
const storageAdapter = {
async load() { return await useStorage('emulate').getItem<string>('state') },
async save(data: string) { await useStorage('emulate').setItem('state', data) },
}
export default defineEventHandler(createEmulateHandler({
services: { github: { emulator: github } },
persistence: storageAdapter,
}))
ファイル永続化
ローカル開発用に、@emulators/core にはファイルベースのアダプターが含まれています。
import { filePersistence } from '@emulators/core'
persistence: filePersistence('.emulate/state.json'),
GitHub App のシードでは private_key を省略できます。ハンドラーはそのまま保持し、サーバー専用の generatedSecrets() を呼び出してください。明示的なキーは除外されます。永続化されたスナップショットは非公開に保ち、initialize をアトミックに実装してください。
永続化の仕組み
- コールドスタート:アダプターは永続化アダプターから状態を読み込みます。状態が見つかった場合は Store 全体とトークンマップを復元します。見つからない場合は設定からシードして、初期状態を保存します。
- 変更を伴うリクエストの後(POST、PUT、PATCH、DELETE):状態が保存されます。競合状態を防ぐため、保存処理は内部キューを介して直列化されます。
- 永続化が設定されていない場合:純粋なメモリ内処理にフォールバックします。シードデータはコールドスタートのたびに再初期化されます。
動作の仕組み
- 受信リクエスト:
/emulate/github/login/oauth/authorize?client_id=... - 解析:service =
github、rest =/login/oauth/authorize - プレフィックスの除去:除去後のパスを持つ新しい
Requestが作成され、GitHub サービスアプリに転送されます - レスポンスの書き換え:HTML の
actionとhref属性、CSS のurl()フォント参照、Locationヘッダーにサービスプレフィックスが付加されます - 永続化:変更を伴うリクエストの後、永続化アダプターを介して状態が保存されます
制限事項
- エミュレーターは Node API を使用するため、Node 互換の Nuxt サーバーランタイムが必要です
- 同時に行われる変更は、後勝ちのセマンティクスを使用します。生成された ID については、コールドスタートをまたいで初期スナップショットをアトミックに選択するため、
initializeが必要です。
設定リファレンス
createEmulateHandler(config, options?)
| フィールド | 型 | 説明 |
|---|---|---|
services | Record<string, EmulatorEntry> | サービス名とエミュレーター設定のマップ |
persistence? | PersistenceAdapter | コールドスタートをまたいで状態を保持するための任意の永続化アダプター |
各 EmulatorEntry:
| フィールド | 型 | 説明 |
|---|---|---|
emulator | EmulatorModule | エミュレーターパッケージ。例:import * as github from '@emulators/github' |
seed? | Record<string, unknown> | サービスの設定スキーマに一致するシードデータ |
オプション:
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
param | string | "path" | 名前付きキャッチオールルートのパラメーター |
routePrefix | string | detected | キャッチオールルートがマウントされるパスプレフィックス |
withEmulate(nuxtConfig)
Nitro の本番トレースに @emulators/core のアセットを含めるよう、Nuxt 設定をラップします。nuxt.config.ts の defineNuxtConfig 内で呼び出してください。
PersistenceAdapter
interface PersistenceAdapter {
load(): Promise<string | null>
save(data: string): Promise<void>
initialize?(data: string): Promise<string>
}
initialize は初期値をアトミックに作成するか、別のインスタンスが先に作成した値を返す必要があります。Redis の SET NX のような compare-and-set セマンティクスで実装してください。@emulators/core の組み込み filePersistence(path) は、ローカル開発向けにこの動作を提供します。
カスタムエミュレーター
アダプターは、services[name].emulator 内の emulate の defineEmulator で作成された定義を受け付けます。CLI/テストから定義を再利用してください。カスタムインスペクターはオプトインです。ルート相対のリダイレクト先はサービスのマウント配下に留まりますが、カスタム HTML ボディはそのまま通過します。テストの終了処理では、返されたハンドラーの close() を待機してください。デプロイされる定義では、emulate をランタイム依存関係としてインストールしてください。シード、スナップショット、永続化、ライフサイクルのルールについては https://emulate.dev/docs/custom-emulators を参照してください。