
emulate
★ 1,500by vercel · part of vercel-labs/emulate
Local drop-in API emulator for Vercel, GitHub, Google, Slack, Apple, Microsoft, and AWS. Use when the user needs to start emulated services, configure seed…
Local drop-in API emulator for Vercel, GitHub, Google, Slack, Apple, Microsoft, and AWS. Use when the user needs to start emulated services, configure seed…
Inspect the full instructions your agent will receiveExpandCollapse
This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.
by vercel
Local drop-in API emulator for Vercel, GitHub, Google, Slack, Apple, Microsoft, and AWS. Use when the user needs to start emulated services, configure seed…
npx skills add https://github.com/vercel-labs/emulate --skill emulate
Download ZIPGitHub1.5k
Service Emulation with emulate
Local drop-in replacement services for CI and no-network sandboxes. Fully stateful, production-fidelity API emulation, not mocks.
CLI
# Start all services (zero-config)
npx emulate
# Start specific services
npx emulate --service vercel,github
# Custom base port (auto-increments per service)
npx emulate --port 3000
# Use a seed config file
npx emulate --seed config.yaml
# Generate a starter config
npx emulate init
# Generate config for a specific service
npx emulate init --service vercel
# List available services
npx emulate list
Options
Flag Default Description
-p, --port 4000 Base port (auto-increments per service)
-s, --service all Comma-separated services to enable
--seed auto-detect Path to seed config (YAML or JSON)
--base-url none Override advertised base URL (supports {service} template)
--portless off Serve over HTTPS via portless (auto-registers aliases)
The port can also be set via EMULATE_PORT or PORT environment variables.
The advertised base URL (used in OAuth redirects, webhook URLs, etc.) can be overridden via --base-url, the EMULATE_BASE_URL env var (supports {service} template), or per-service baseUrl in the seed config. When running under portless, the PORTLESS_URL env var is also detected automatically.
Programmatic API
npm install emulate
Each call to createEmulator starts a single service:
import { createEmulator } from 'emulate'
const github = await createEmulator({ service: 'github', port: 4001 })
const vercel = await createEmulator({ service: 'vercel', port: 4002 })
github.url // 'http://localhost:4001'
vercel.url // 'http://localhost:4002'
await github.close()
await vercel.close()
Options
Option Default Description
service (required) 'vercel', 'github', 'google', 'slack', 'apple', 'microsoft', 'okta', 'aws', 'resend', 'stripe', 'mongoatlas', 'clerk', 'linear', or 'twilio'
port 4000 Port for the HTTP server
seed none Inline seed data (same shape as YAML config)
baseUrl none Override advertised base URL. Per-service baseUrl in seed config takes highest priority, then this option, then EMULATE_BASE_URL env var (supports {service}), then PORTLESS_URL (supports {service}, automatically set by the portless CLI wrapper), then http://localhost:<port>.
Instance Methods
Method Description
url Base URL of the running server
reset() Wipe the store and replay seed data
close() Shut down the HTTP server, returns a Promise
HTTPS with portless
portless gives emulators trusted HTTPS URLs with auto-generated certs. Use the --portless flag to auto-register each service as a portless alias:
npx emulate start --portless
# github https://github.emulate.localhost
# google https://google.emulate.localhost
# ...
This requires the portless proxy to be running (portless proxy start). If portless is not installed, emulate will prompt to install it.
The --portless flag overwrites any existing portless aliases matching *.emulate. Aliases are removed automatically when emulate shuts down.
For a single service behind portless:
portless github.emulate emulate start --service github
For a custom base URL without portless (any reverse proxy):
npx emulate start --base-url "https://{service}.myproxy.test"
# or
EMULATE_BASE_URL="https://{service}.myproxy.test" npx emulate start
The PORTLESS_URL env var is automatically set by the portless CLI wrapper when running a command through it (e.g. portless github.emulate emulate start), typically to a value like https://{service}.emulate.localhost. It supports {service} interpolation, just like --base-url and EMULATE_BASE_URL. When no explicit baseUrl is provided, it is used as a fallback.
Per-service overrides in the seed config (these take highest priority over all other base URL sources):
github:
baseUrl: https://github.emulate.localhost
google:
baseUrl: https://google.emulate.localhost
Pointing Your App at the Emulator
Set environment variables to override real service URLs:
VERCEL_EMULATOR_URL=http://localhost:4000
GITHUB_EMULATOR_URL=http://localhost:4001
GOOGLE_EMULATOR_URL=http://localhost:4002
SLACK_EMULATOR_URL=http://localhost:4003
APPLE_EMULATOR_URL=http://localhost:4004
MICROSOFT_EMULATOR_URL=http://localhost:4005
AWS_EMULATOR_URL=http://localhost:4007
LINEAR_EMULATOR_URL=http://localhost:4012
Then use these in your app to construct API and OAuth URLs. See each service's skill for SDK-specific override instructions.
Framework Integration (Embedded Mode)
The @emulators/adapter-next package embeds emulators directly into a Next.js app on the same origin. See the next skill (skills/next/SKILL.md) for full setup, Auth.js configuration, persistence, and font tracing details.
The @emulators/adapter-nuxt package embeds emulators directly into a Nuxt app on the same origin. See the nuxt skill (skills/nuxt/SKILL.md) for the server route, Nuxt config, OAuth configuration, and persistence setup.
Persistence
By default, all emulator state is in-memory. For persistence across process restarts and serverless cold starts, use a PersistenceAdapter.
Built-in file persistence
import { filePersistence } from '@emulators/core'
// CLI or local dev: persists to a JSON file
const adapter = filePersistence('.emulate/state.json')
Custom adapters
import type { PersistenceAdapter } from '@emulators/core'
const kvAdapter: PersistenceAdapter = {
async load() { return await kv.get('emulate-state') },
async save(data) { await kv.set('emulate-state', data) },
}
State is loaded on cold start and saved after every mutating request (POST, PUT, PATCH, DELETE). Saves are serialized to prevent race conditions.
Architecture
packages/
emulate/ # CLI entry point + programmatic API
@emulators/
core/ # HTTP server, Store, plugin interface, middleware
adapter-next/ # Next.js App Router integration
adapter-nuxt/ # Nuxt server route integration
vercel/ # Vercel API service plugin
github/ # GitHub API service plugin
google/ # Google OAuth 2.0 / OIDC plugin
slack/ # Slack Web API, OAuth, incoming webhooks plugin
linear/ # Linear GraphQL API, OAuth, webhooks plugin
twilio/ # Twilio Messaging, Verify, Voice, webhooks plugin
apple/ # Sign in with Apple / OIDC plugin
microsoft/ # Microsoft Entra ID OAuth 2.0 / OIDC plugin
aws/ # AWS S3, SQS, IAM, STS plugin
The core provides a generic Store with typed Collection<T> instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers routes with the shared internal app and uses the store for state.
npx emulateRun this in your project — your agent picks the skill up automatically.
Quick Start
npx emulate
All services start with sensible defaults:
Service Default Port Vercel 4000 GitHub 4001 Google 4002 Slack 4003 Apple 4004 Microsoft 4005 Okta 4006 AWS 4007 Resend 4008 Stripe 4009 MongoDB Atlas 4010 Clerk 4011 Linear 4012 Twilio 4013
Vitest / Jest Setup
import { createEmulator, type Emulator } from 'emulate'
let github: Emulator
let vercel: Emulator
beforeAll(async () => {
;[github, vercel] = await Promise.all([
createEmulator({ service: 'github', port: 4001 }),
createEmulator({ service: 'vercel', port: 4002 }),
])
process.env.GITHUB_EMULATOR_URL = github.url
process.env.VERCEL_EMULATOR_URL = vercel.url
})
afterEach(() => { github.reset(); vercel.reset() })
afterAll(() => Promise.all([github.close(), vercel.close()]))
Configuration
Configuration is optional. The CLI auto-detects config files in this order:
-
emulate.config.yaml/.yml -
emulate.config.json -
service-emulator.config.yaml/.yml -
service-emulator.config.json
Or pass --seed <file> explicitly. Run npx emulate init to generate a starter file.
Config Structure
tokens:
my_token:
login: admin
scopes: [repo, user]
vercel:
users:
- username: developer
name: Developer
email: [email protected]
teams:
- slug: my-team
name: My Team
projects:
- name: my-app
team: my-team
framework: nextjs
integrations:
- client_id: oac_abc123
client_secret: secret_abc123
name: My Vercel App
redirect_uris:
- http://localhost:3000/api/auth/callback/vercel
github:
users:
- login: octocat
name: The Octocat
email: [email protected]
orgs:
- login: my-org
name: My Organization
repos:
- owner: octocat
name: hello-world
language: JavaScript
auto_init: true
oauth_apps:
- client_id: Iv1.abc123
client_secret: secret_abc123
name: My Web App
redirect_uris:
- http://localhost:3000/api/auth/callback/github
google:
users:
- email: [email protected]
name: Test User
oauth_clients:
- client_id: my-client-id.apps.googleusercontent.com
client_secret: GOCSPX-secret
redirect_uris:
- http://localhost:3000/api/auth/callback/google
slack:
team:
name: My Workspace
domain: my-workspace
users:
- name: developer
real_name: Developer
email: [email protected]
channels:
- name: general
topic: General discussion
bots:
- name: my-bot
oauth_apps:
- client_id: "12345.67890"
client_secret: example_client_secret
name: My Slack App
redirect_uris:
- http://localhost:3000/api/auth/callback/slack
linear:
organization:
name: Acme
url_key: acme
users:
- email: [email protected]
name: Admin User
admin: true
- email: [email protected]
name: Developer
teams:
- key: ENG
name: Engineering
issues:
- team: ENG
title: Fix local checkout test
state: Todo
assignee: [email protected]
oauth_apps:
- client_id: lin_example_client_id
client_secret: example_client_secret
name: My Linear App
redirect_uris:
- http://localhost:3000/api/auth/callback/linear
scopes: [read, write, issues:create, comments:create]
tokens:
- token: lin_test_admin
user: [email protected]
scopes: [read, write, issues:create, comments:create, admin]
apple:
users:
- email: [email protected]
name: Test User
oauth_clients:
- client_id: com.example.app
team_id: TEAM001
name: My Apple App
redirect_uris:
- http://localhost:3000/api/auth/callback/apple
microsoft:
users:
- email: [email protected]
name: Test User
oauth_clients:
- client_id: example-client-id
client_secret: example-client-secret
name: My Microsoft App
redirect_uris:
- http://localhost:3000/api/auth/callback/microsoft-entra-id
aws:
region: us-east-1
s3:
buckets:
- name: my-app-bucket
sqs:
queues:
- name: my-app-events
iam:
users:
- user_name: developer
create_access_key: true
roles:
- role_name: lambda-execution-role
Auth
Tokens map to users. Pass them as Authorization: Bearer <token> or Authorization: token <token>. When no tokens are configured, a default test_token_admin is created for the admin user.
Each service also has a fallback user. If no token is provided, requests authenticate as the first seeded user.
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.