API & database emulation
Local emulators.
Real API contracts.
Mockingbird provides stateful emulators for third-party APIs and SQL databases. Use vendor-compatible request and response shapes, persistent in-memory state, and deterministic controls in Node, Bun, browsers, and Workers.
npm install -D @crvouga/mockingbird-service-stripepnpm add -D @crvouga/mockingbird-service-stripeyarn add -D @crvouga/mockingbird-service-stripebun add -d @crvouga/mockingbird-service-stripeNode.js · Bun · Browser · Workers
import Stripe from "stripe"
import { createRuntime } from
"@crvouga/mockingbird-service-stripe"
const emulator = createRuntime()
// Your Stripe SDK. Only its HTTP client changes.
const stripe = new Stripe("sk_test_mockingbird", {
httpClient: Stripe.createFetchHttpClient(
(url: string, init: RequestInit) =>
emulator.fetch(new Request(url, init)),
),
})
const customer = await stripe.customers.create({
email: "ada@example.com",
})/v1/customers200 OKA test environment with nothing outside it.
Tests that call live sandboxes wait on every vendor and fail when one is slow or down. Mockingbird runs an emulator of each service inside your process, so there is no network left to wait on.
- charges the card
Stripe480 ms
- texts the receipt
Twilio1.2 s
- summarizes the order
OpenAI6.4 s
- posts to #orders
Slackretried2.9 s
- emails the invoice
Resend730 ms
- stores the PDF
Amazon S3timeout30.0 s
- charges the card
Stripe2 ms
- texts the receipt
Twilio1 ms
- summarizes the order
OpenAI3 ms
- posts to #orders
Slack1 ms
- emails the invoice
Resend2 ms
- stores the PDF
Amazon S33 ms
From product data to a paid order.
Build a checkout with PostgreSQL and Stripe emulators. Seed the data, write an API against the unmodified Stripe SDK, and place an order from a client. Every step runs in your process.
npm install -D @crvouga/mockingbird-service-postgres @crvouga/mockingbird-service-stripepnpm add -D @crvouga/mockingbird-service-postgres @crvouga/mockingbird-service-stripeyarn add -D @crvouga/mockingbird-service-postgres @crvouga/mockingbird-service-stripebun add -d @crvouga/mockingbird-service-postgres @crvouga/mockingbird-service-stripeSeed a product catalog and an orders table in the PostgreSQL emulator.
import { Database } from "@crvouga/mockingbird-service-postgres"
export const db = new Database()
db.exec(`
CREATE TABLE products (id text PRIMARY KEY, name text, price integer);
CREATE TABLE orders (id text PRIMARY KEY, product_id text, payment_id text);
INSERT INTO products VALUES ('starter-kit', 'Developer starter kit', 2900);
`)
// Real SQL, in memory. No database server to configure.
console.log(db.query("SELECT name, price FROM products"))
// [{ name: "Developer starter kit", price: 2900 }]Read a product, charge it with the Stripe SDK, and persist the order. The emulator answers the SDK in this process.
import Stripe from "stripe"
import { createRuntime } from "@crvouga/mockingbird-service-stripe"
import { db } from "./data.ts"
// The Stripe SDK your application already uses. Only its HTTP client changes:
// requests reach the emulator in this process instead of api.stripe.com.
const emulator = createRuntime()
export const stripe = new Stripe("sk_test_mockingbird", {
httpClient: Stripe.createFetchHttpClient((url: string, init: RequestInit) =>
emulator.fetch(new Request(url, init)),
),
})
export const api = {
async fetch(request: Request) {
if (request.method !== "POST" || new URL(request.url).pathname !== "/orders") {
return Response.json({ error: "Not found" }, { status: 404 })
}
const { productId } = await request.json()
const [product] = db.query<{ price: number }>(
"SELECT price FROM products WHERE id = $1", [productId],
)
if (!product) return Response.json({ error: "Product not found" }, { status: 404 })
const intent = await stripe.paymentIntents.create({
amount: product.price, currency: "usd",
payment_method: "pm_card_visa", confirm: true,
})
if (intent.status !== "succeeded") {
return Response.json({ paymentStatus: intent.status }, { status: 402 })
}
const orderId = crypto.randomUUID()
db.prepare("INSERT INTO orders VALUES ($1, $2, $3)").run(orderId, productId, intent.id)
return Response.json({ orderId, paymentStatus: intent.status }, { status: 201 })
},
}Place an order through the application's Fetch handler. The whole flow stays local.
import { api } from "./api.ts"
// The same Request / Response interface your client already uses.
export const response = await api.fetch(new Request("http://app.local/orders", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ productId: "starter-kit" }),
}))
export const order = await response.json()
console.log(response.status) // 201
console.log(order.paymentStatus) // "succeeded"
// Product read → payment confirmed → order saved.
// PostgreSQL + Stripe + your API, without a single network request.201 CreatedPayment confirmed. Order saved.Zero network requestsSave all three files next to the stripe SDK your app already has, then run bun client.ts or npx tsx client.ts. Follow the walkthrough
Reproducible behavior, with explicit coverage.
Emulate the operations your application uses, inspect supported behavior, and control state between runs.
The vendor's real surface
Each emulator answers the provider's own paths, headers, status codes and error envelopes through fetch(Request) → Response. Point the official SDK at it.
State that behaves
Records persist in an in-memory SQL engine. Created customers can be listed, orders move through their lifecycle, webhooks fire, and reset or snapshot takes one call.
Checked against the real thing
Random walks generated from each vendored OpenAPI contract run against two emulator instances in CI, and against the live sandbox when credentials exist.
No network, no waiting
Everything runs in your test process. No sandbox keys, rate limits, shared test accounts or flaky round trips.
Runs anywhere JavaScript runs
Every emulator is isomorphic: the same package runs in Node, Bun, browsers, and Workers. The playgrounds on this site run that package in your browser tab.
One set of controls for every service
Every HTTP emulator takes the same admin controls: isolation by key, reset, snapshot and restore, time travel, faults, latency and timeouts, all replayed from a seed. Set them at boot, through /__admin, or in a request header.
Put any emulator in any state. Then do it again.
Every HTTP emulator answers the same admin controls. Isolate state by key, reset it, snapshot and restore it, move the clock, and inject faults, latency and timeouts. Each one is a field in a single document that means the same thing at boot, over /__admin, and in a request header, and all of it replays from a seed.
const stripe = createRuntime({
seed: 7,
adminControls: {
clock: {
now: "2030-01-01T00:00:00Z",
frozen: true,
},
latency: "40ms",
faults: [{ preset: "rate_limited" }],
},
})curl -X POST :8787/__admin/snapshots \
-d '{"name": "base"}'
curl -X POST :8787/__admin/clock \
-d '{"advance": "15m"}'
curl -X POST :8787/__admin/faults \
-d '{"status": 503, "count": 2}'
curl -X POST :8787/__admin/snapshots/base/restorePOST /v1/charges
x-mockingbird-namespace: worker-3
x-mockingbird-time: 2030-01-01T00:00:00Z
x-mockingbird-fault: status=503; delay=2sIsolation by key
x-mockingbird-namespace: worker-3 gives a request its own records, clock, random streams, faults, latency, journal and snapshots. Parallel workers share one process and never see each other.
Reset
POST /__admin/reset returns a namespace to its boot state: seed data, clock, faults and ids. A reset namespace cannot be told apart from a new one.
Snapshot and restore
POST /__admin/snapshots captures records, the clock, the random streams and the fault counters. Restore it, export it as JSON, commit it, or boot another process from it.
Time travel
Freeze the clock, set it, or advance it by 15m. Expiries and retry windows arrive in milliseconds. Read state as it was at any checkpoint, or branch from one.
Faults
Answer 503, drop the connection, or cut a response off mid-body. Match by operation, path or header; fire on the third request, every fifth, for the next five minutes, or at a seeded rate.
Latency and timeouts
Give every response a delay with seeded jitter, or make one request hang. With virtualTime the wait is spent on the mock clock, so a thirty-second timeout costs no test time.
Deterministic, down to the journal
The same seed and the same requests give the same bytes: ids, timestamps, which requests a rate fault hits, and the request journal. Reset and restore rewind the random streams with the state, so a test passes or fails the same way whether it runs first or last, alone or beside forty workers.
Find your application's dependencies.
Featured emulators
View featuredStart with payments, SQL, object storage, or healthcare integrations. Open an emulator to explore its contract and try the playground.
Amazon S3
StorageStateful S3 emulator that answers exactly as MinIO does: every bucket, object, multipart, versioning, object-lock, policy and auth API, verified against recorded MinIO transcripts.
Junction
Health & labsStateful emulator of the Junction (Vital) user API driven by its OpenAPI contract and verified by differential property tests against the Junction sandbox.
PostgreSQL
DatabasesPure TypeScript in-memory PostgreSQL implementation with zero WASM/native dependencies
Stripe
PaymentsStateful emulator of the Stripe API (accounts by key, customers, payments, subscriptions that renew, pause, resume and cancel, invoices, checkout with a hosted page, a hosted customer portal, a Stripe.js stand-in, signed webhooks) at API versions 2024-06-20 and 2025-02-24.acacia, verified by differential property tests against Stripe test mode.
Infrastructure
11Container engines and orchestration APIs.
Payments
4Charges, subscriptions, checkout and HSA/FSA billing.
Health & labs
16EHRs, lab ordering, diagnostics, telehealth, nutrition.
Pharmacy
5eRx, compounding pharmacies and fulfilment.
Communication
20Email, SMS, chat, video and inbox testing.
Marketing
7Customer data, campaigns and referral tracking.
AI & speech
7LLM runtimes, retrieval, speech synthesis and transcription.
Observability
8Product analytics, feature flags and telemetry.
Productivity
10Calendars, issue trackers, CMS and surveys.
Maps & logistics
2Places, geocoding and shipment tracking.
Identity
19Identity verification and KYC.
Databases
4In-memory SQL engines with real dialect semantics.
Storage
12Object and file storage APIs for binary test fixtures.
A complete application, running in your browser.
The client, server, database emulator, and API emulators share one browser tab. Explore the application and its state without configuring a server.
OAuthGoogle & Apple sign-inJunctionLab results
StripePayments
PostgreSQLDatabase
- Run it in this tab, with nothing to install
- Serve it with
bun run devbehind a real port - Test it with
bun test, routes driven in-process
A shared control plane for every HTTP emulator.
Every HTTP service ships an in-process fetch, a Node server and a CLI, and all answer the same control surface, so a stack learns it once. Every internal path is under /__admin by default. Set adminPrefix or serve --admin-prefix (MOCKINGBIRD_ADMIN_PREFIX) to relocate the entire tree; /health and /ns aliases are removed.
| Interface | Behavior |
|---|---|
createRuntime() · createServer() (./server) · mockingbird-<service> serve | The emulator as one runtime-neutral fetch, or a listening server from Node or the CLI |
GET /__admin/health | Unauthenticated readiness probe, outside the vendor's auth gate |
/__admin/* (x-mockingbird-admin-key optional) | The admin controls: reset, snapshot and restore, clock, faults, latency, a request journal, metrics with unmatched-route counts, plus service-specific routes. GET /__admin lists every route and header |
GET /__admin/state | The collections in the selected namespace: declared shape, live Collection fields, and stored rows. Create, replace, merge, and delete records through the same paths on every emulator |
GET /__admin/ui | The shared admin UI. An emulator can add panels or replace the document; the shell still reads the same state API. Its header fetches that service's logo, website, and docs from the docs site when the page opens |
x-mockingbird-namespace | Per-request isolation: parallel workers share one process without sharing data, clocks, faults or ids |
adminControls · --admin-controls · x-mockingbird-* | The same control document at boot, on the command line, and in request headers |
--seed, a frozen clock, virtualTime | Seeded randomness, a clock that moves only when told to, and waits that cost no time, so a run replays byte for byte |
--log json | One structured line per request: operation id, status, duration, namespace, fault |
mockingbird.json names services by their package suffix. HTTP services, PostgreSQL and Redis share a supervisor with ephemeral ports, readiness discovery and namespace controls. Use --ready-file or --ready-json to discover endpoints; see Fleets. SQLite remains an in-process engine.
Source-backed guides for developers and agents.
Every npm package ships an agent index at node_modules/<package>/DISCOVERY.md. It points to the local behavior guide, exact capability matrix, machine-readable contract or compatibility evidence, public types, parity oracle, runtime introspection, and issue-reporting contract. llms.txt indexes those files with the parity each service declares, and the docs site publishes the same source material as markdown and JSON. When an emulator diverges from the real API, lacks a feature you call, or the vendor you need is not in the catalog, file an issue: the filing guide gives the title format, templates and the behavior spec for feature and service requests.