Programmatic API
Use emulators as libraries in your tests and scripts. There are two approaches: the api-emulator CLI package (which wraps all services) and the individual @api-emulator/* scoped packages.
CLI package
The api-emulator package provides a createEmulator helper that starts any service by name.
npm install api-emulator
import { createEmulator } from 'api-emulator'
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()
Vitest / Jest setup
// vitest.setup.ts
import { createEmulator, type Emulator } from 'api-emulator'
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()]))
createEmulator options
| Option | Default | Description |
|---|---|---|
service | (required) | Service name: 'vercel', 'github', 'google', 'slack', 'apple', 'microsoft', 'aws', 'okta', 'mongoatlas', 'resend', or 'stripe' |
port | 4000 | Port for the HTTP server |
seed | none | Inline seed data (same shape as YAML config) |
baseUrl | none | Override the advertised base URL (used in OAuth redirects, webhook URLs, etc.). Per-service baseUrl in seed config takes highest priority, then this option, then the API_EMULATOR_BASE_URL env var (supports {service} template), 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 |
snapshot() | Capture the current store snapshot |
restore(fixture) | Restore a store snapshot or exported fixture |
exportFixture(options?) | Export a fixture envelope with store state and recorded interactions |
resetToFixture(fixture) | Reset state to a captured fixture |
reset() | Wipe the store and replay seed data |
close() | Shut down the HTTP server, returns a Promise |
Fixtures
Use fixtures to stabilize a stochastic or stateful run:
const fixture = github.exportFixture({ metadata: { name: 'pull-request-flow' } })
github.resetToFixture(fixture)
Scoped packages
Each emulator is published as its own @api-emulator/* package. Install only the ones you need:
npm install @api-emulator/github @api-emulator/google @api-emulator/stripe
Available packages
| Package | Service |
|---|---|
@api-emulator/vercel | Vercel API |
@api-emulator/github | GitHub API |
@api-emulator/google | Google OAuth, Gmail, Calendar, Drive |
@api-emulator/slack | Slack Web API, OAuth v2, webhooks |
@api-emulator/apple | Apple Sign In / OIDC |
@api-emulator/microsoft | Microsoft Entra ID, Graph API |
@api-emulator/aws | AWS S3, SQS, IAM, STS |
@api-emulator/okta | Okta identity provider / OIDC |
@api-emulator/mongoatlas | MongoDB Atlas Admin API + Data API |
@api-emulator/resend | Resend email API |
@api-emulator/stripe | Stripe billing and payments API |
@api-emulator/core | Shared store, middleware, and utilities |
@api-emulator/adapter-next | Next.js App Router integration |
Direct usage
Each scoped package exports its Hono app and seed function, so you can compose emulators however you like:
import * as github from '@api-emulator/github'
import * as stripe from '@api-emulator/stripe'
These are the same modules used internally by the CLI and the Next.js adapter. See the Next.js Integration page for a full example of using scoped packages with createApiEmulatorHandler.