cf-api-sim
v0.1.0
Published
Local Cloudflare REST API simulator — mock the /client/v4 surface (D1, R2, KV, Workers Scripts) on your laptop, backed by SQLite + the filesystem. Point any Cloudflare SDK, wrangler, or REST client at it via CLOUDFLARE_API_BASE_URL.
Maintainers
Readme
cf-api-sim
A local simulator of the Cloudflare REST API (/client/v4). Run D1, R2, KV, Workers Scripts and the GraphQL analytics endpoint on your own machine — point any Cloudflare SDK, wrangler, or raw REST client at it for fast, offline local development and integration tests. No Cloudflare account, no network, no quotas.
If you've ever wanted a Cloudflare API mock for testing — a local D1/R2/KV emulator that speaks the real REST shapes, lets you run wrangler offline against fake resources, or exercise your client's retry and error-handling paths deterministically — that's what this is.
What it covers
cf-api-sim implements the commonly-used subset of the Cloudflare REST API surface:
- D1 — create / list / get / delete databases;
POST /query(single statement, results-as-objects), batch queries (array body, run atomically in one transaction), andPOST /raw(results-as-2D-array). Backed by a real per-database SQLite file, so your SQL actually runs. - R2 — create / list / get / delete buckets; object PUT / GET / DELETE / list; bucket lifecycle get/set; plus an S3-compatible endpoint (
/<bucket>/<key>,?list-type=2) so S3 clients can read and write objects. - KV — create / list / rename / delete namespaces; get / put / delete values (raw bytes, with metadata and expiration); list keys with prefix + cursor pagination; and
bulk/get,bulkPUT,bulkDELETE. - Workers Scripts — upload (
PUT /scripts/:name, multipart), get (JSON or multipart round-trip), service existence check, and delete. - GraphQL analytics —
POST /client/v4/graphqlreturns shape-valid empty aggregates, so "no data yet" rendering paths in your app still exercise cleanly. - Fault injection — arm canned error responses and artificial delays on specific routes so you can test client retry and error handling deterministically (see Fault injection).
State is persisted on disk: a control-plane SQLite database for metadata, one SQLite file per simulated D1 database (via better-sqlite3), and R2 objects on the filesystem. No real Cloudflare account or network is involved.
Quick start
npx cf-api-simThat's it. The simulator binds to http://127.0.0.1:8745 (loopback only) and prints its health URL on startup:
[cf-api-sim] listening on http://127.0.0.1:8745
[cf-api-sim] data dir: ./data
[cf-api-sim] health: http://127.0.0.1:8745/sim/healthCheck it's up:
curl http://127.0.0.1:8745/sim/health
# { "ok": true, "name": "cf-api-sim", "version": "...", ... }Install as a dev dependency in your project:
npm i -D cf-api-simConfiguration
| Env var | Default | Description |
| -------------- | -------- | -------------------------------------------- |
| SIM_PORT | 8745 | Port to listen on. |
| SIM_DATA_DIR | ./data | Directory for persisted control DB, D1 files, and R2 objects. |
SIM_PORT=9000 SIM_DATA_DIR=/tmp/cf-sim npx cf-api-simPointing a client at the simulator
Auth is shape-only — the simulator accepts any non-empty bearer token. Use any account ID you like; one default account (dev-acct) is always present.
Cloudflare SDK (cloudflare)
The official cloudflare SDK accepts a baseURL option:
import Cloudflare from "cloudflare";
const client = new Cloudflare({
apiToken: "any-non-empty-string",
baseURL: "http://127.0.0.1:8745/client/v4",
});
const dbs = await client.d1.database.list({ account_id: "dev-acct" });Environment variable
Many tools (and the SDK) read CLOUDFLARE_API_BASE_URL:
export CLOUDFLARE_API_BASE_URL=http://127.0.0.1:8745/client/v4
export CLOUDFLARE_API_TOKEN=any-non-empty-stringRaw REST
curl -X POST http://127.0.0.1:8745/client/v4/accounts/dev-acct/d1/database \
-H "Authorization: Bearer dev-token" \
-H "Content-Type: application/json" \
-d '{"name":"my-db"}'S3-compatible R2 endpoint
S3 clients can talk to R2 objects directly at http://127.0.0.1:8745/<bucket>/<key>. The account ID is taken from the Host header (canonical <account>.r2.cloudflarestorage.com shape) or from an x-sim-account-id header for clients that can't set a custom host (e.g. curl). A SigV4-shaped Authorization header is required, but the signature is not verified (see Limitations & security).
Fault injection (for tests)
The /sim/* control endpoints let you make the simulator misbehave on demand — perfect for testing how your client handles errors, timeouts, and retries. They require no auth and are never themselves affected by injected faults.
| Endpoint | Method | Purpose |
| --------------------- | ------ | ------------------------------------------------------------- |
| /sim/health | GET | Liveness check. |
| /sim/fail-next | POST | Arm a canned status response for the next matching request. |
| /sim/delay-next | POST | Arm an artificial delay (no status override) for the next match.|
| /sim/reset | POST | Clear all armed faults and the call log. |
| /sim/calls | GET | Inspect the recent request log. |
| /sim/faults | GET | Inspect the armed-fault queue. |
Make the next D1 create return a 500:
curl -X POST http://127.0.0.1:8745/sim/fail-next \
-H "Content-Type: application/json" \
-d '{"method":"POST","path":"/client/v4/accounts/dev-acct/d1/database","status":500}'fail-next accepts method, path, status (required), plus optional body, contentType, count (how many requests to affect, default 1), and delayMs. delay-next accepts method, path, delayMs (required), plus optional count.
Reset between test cases:
curl -X POST http://127.0.0.1:8745/sim/resetLimitations & security
cf-api-sim is a development and testing tool. It is intentionally not a faithful or secure reproduction of Cloudflare.
- Loopback only. The server binds to
127.0.0.1. Do not expose it to a network or run it on a shared/public host. - No real authentication. The data plane accepts any non-empty bearer token — there are no real accounts, tokens, or permissions behind it. The S3 endpoint trusts the
x-sim-account-idheader and the shape of a SigV4Authorizationheader without verifying the signature. This is not an auth or permissions simulator — anyone who can reach the port has full access, which is exactly why it must stay on loopback. - No request-body size limits. Don't point untrusted input at it.
- Partial API surface. It implements the commonly-used subset of each API, not the full Cloudflare surface. Unimplemented endpoints return a
501with a clear "not implemented" message so you can tell sim gaps from real failures. - Persistent state. Resources, values, and objects persist under the data directory between runs. Delete it (or point
SIM_DATA_DIRat a temp dir) for a clean slate.
See SECURITY.md for the security posture and reporting details.
Related projects
- cf-wfp-simulator — a sibling project: a local simulator for Cloudflare Workers for Platforms (dispatch namespaces / tenant workers).
- teenybase — the project these simulators came from: backend-as-config on Cloudflare Workers + D1.
Contributing
Contributions are welcome — see CONTRIBUTING.md for local setup, testing, and PR conventions.
License
Apache-2.0 © Palash Bansal <[email protected]>
Suggested GitHub topics: cloudflare · d1 · r2 · kv · workers · simulator · mock · testing · local-development
Keywords: Cloudflare API mock, Cloudflare REST API simulator, local D1 emulator, R2 emulator, KV emulator, wrangler offline, Cloudflare integration testing.
