Fake real APIs locally so your app can test integrations without touching production, sandboxes, or someone else's server.
api-emulator is a local app store for fake APIs. Run GitHub, Stripe, Resend, and plugin powered providers on localhost, seed state, inspect behavior, reset data, and test your app in one place.
- Test API integrations locally without real provider credentials.
- Run multiple fake services together, with shared state, auth, webhooks, seed data, and resets.
- Keep provider behavior in plugins so public, private, and internal APIs can live outside your app.
npx -p api-emulator api
npx -p api-emulator api --service github,stripe,resendThen point your app at the local provider URLs:
http://localhost:4000/github
http://localhost:4000/stripe
http://localhost:4000/resend
Use trusted local HTTPS names when your app needs browser compatible origins:
npx -p api-emulator api --service github,stripe,resend --portlesshttps://github.api-emulator.localhost
https://stripe.api-emulator.localhost
https://resend.api-emulator.localhost
Create starter config and list available services:
npx -p api-emulator api init
npx -p api-emulator api listimport { createEmulator } from 'api-emulator'
const github = await createEmulator({ service: 'github', port: 4001 })
process.env.GITHUB_API_BASE = github.url
afterEach(() => github.reset())
afterAll(() => github.close())Capture and replay a stable fixture after a stochastic or stateful run:
const fixture = github.exportFixture({ metadata: { name: 'pull-request-flow' } })
github.resetToFixture(fixture)Install more providers from a public or internal plugin shelf:
npx -p api-emulator api install posthog
npx -p api-emulator api install pepper --no-package-managerOr load a plugin file directly:
npx -p api-emulator api --plugin ./api-emulator-plugins/@posthog/api-emulator.mjs --service posthogThe installer auto discovers sibling api-emulator-plugins and api-emulator-internal checkouts. Set API_EMULATOR_PLUGIN_CATALOGS=/path/to/shelf,/path/to/internal to add more shelves.
A plugin exports a ServicePlugin:
import type { ServicePlugin } from '@api-emulator/core'
export const plugin: ServicePlugin = {
name: 'internal-billing',
register(app) {
app.get('/v1/customers', (c) => c.json({ data: [] }))
},
}npm install @api-emulator/adapter-next @api-emulator/coreimport { createEmulateHandler } from '@api-emulator/adapter-next'
import type { ServicePlugin } from '@api-emulator/core'
const internalPlugin: ServicePlugin = {
name: 'internal',
register(app) {
app.get('/health', (c) => c.json({ ok: true }))
},
}
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
services: {
internal: { emulator: { plugin: internalPlugin } },
},
})npx -p api-emulator api init creates api-emulator.config.yaml.
tokens:
test_token_admin:
login: admin
scopes: [repo, user]
github:
users:
- login: octocat
name: The OctocatThe CLI auto-detects api-emulator.config.yaml, .yml, and .json. Older emulate.config.* and service-emulator.config.* names still work as migration aliases.
bun install
bun run build
bun run format:check
bun run type-check
bun run lint
bun run testMIT
