@emulators/adapter-nuxt

Nuxt server route integration for emulate. Embed emulators directly into your Nuxt app so they run on the same origin, which keeps OAuth callbacks stable for preview deployments.
1,447 1,940
Install
npm install @emulators/adapter-nuxt
Latest Version:0.12.3
License:Apache-2.0
Last Updated:Oct 22, 2026
Links: GitHub  ·  npm
Maintainer: vercel-release-bot

@emulators/adapter-nuxt

Nuxt server route integration for emulate. Embed emulators directly into your Nuxt app so they run on the same origin, which keeps OAuth callbacks stable for preview deployments.

Install

npm install @emulators/adapter-nuxt

Only install the emulators you need alongside the adapter:

npm install @emulators/adapter-nuxt @emulators/github @emulators/google

Server Route

Create a named catch-all route that serves emulator traffic:

// server/routes/emulate/[...path].ts
import { createEmulateHandler } from '@emulators/adapter-nuxt'
import * as github from '@emulators/github'
import * as google from '@emulators/google'

export const emulator = 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: 'test@example.com', name: 'Test User' }],
      },
    },
  },
})

export default defineEventHandler(emulator)

This creates these routes:

  • /emulate/github/** serves the GitHub emulator
  • /emulate/google/** serves the Google emulator

GitHub App seeds may omit private_key. Read generated keys with the handler's server-only generatedSecrets() method. Explicit keys are excluded; persisted snapshots contain generated keys and require a private backend with atomic initialize.

Inspect minted installation-token metadata at /emulate/github/_emulate/installation-tokens.

Nuxt Config

Emulator UI pages use bundled fonts. Wrap your Nuxt config so Nitro traces the core package assets into production builds:

// nuxt.config.ts
import { withEmulate } from '@emulators/adapter-nuxt'

export default defineNuxtConfig(withEmulate({
  // your normal Nuxt config
}))

OAuth Configuration

Point your OAuth provider at the emulator paths on the same origin:

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`,
}

No oauth_apps need to be seeded. When none are configured, the emulator skips client_id, client_secret, and redirect_uri validation.

Persistence

By default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a persistence adapter.

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,
}))

For local development, @emulators/core ships a file-based adapter:

import { filePersistence } from '@emulators/core'

persistence: filePersistence('.emulate/state.json'),

Custom Route Param

The adapter reads the path param from server/routes/emulate/[...path].ts. If you use a different catch-all name, pass it as the second argument:

export default defineEventHandler(createEmulateHandler(config, { param: 'slug' }))

If the mount path cannot be detected from the URL, pass routePrefix:

export default defineEventHandler(createEmulateHandler(config, { routePrefix: '/api/emulate' }))

Custom emulators

Pass a definition created with defineEmulator from emulate to services[name].emulator in framework adapters. The same definition runs through the CLI and createEmulator({ service: definition, listen: false }) in tests. Custom entries accept an optional inspector and persistence adapter. Root-relative redirects stay under the service mount, while custom HTML bodies pass through unchanged. Keep state inside the definition's state factory, and await the handler's close() in tests. See the custom emulator guide.

Version History

Version Released License
0.12.3 Apache-2.0
0.12.2 Apache-2.0
0.12.1 Apache-2.0
0.12.0 Apache-2.0
0.11.2 Apache-2.0