clerk-emulator
v1.0.1
Published
Local, offline emulator for Clerk's Backend and Frontend (FAPI) APIs. Develop and test Clerk authentication without the cloud — real JWT signing, M2M tokens, organizations, and webhooks.
Maintainers
Readme
clerk-emulator
A local, offline emulator for Clerk's Backend API (BAPI) and Frontend API (FAPI). Develop and test Clerk auth with no network, no cloud instance, and no rate limits — it signs real JWTs, so @clerk/backend and clerk-js work against it unmodified. Built on Hono; state is in-memory and resets every run.
Install
npm install --save-dev clerk-emulatorQuick start
Run the server:
npx clerk-emulator
# Backend API: http://localhost:4000
# Secret key: sk_test_emulateConfigure via env: PORT, HOST, CLERK_SECRET_KEY, CLERK_MACHINE_KEY. Add --persist <file> (or CLERK_PERSIST) to keep state across restarts.
Point the backend SDK at it:
import { createClerkClient } from "@clerk/backend";
const clerk = createClerkClient({ secretKey: "sk_test_emulate", apiUrl: "http://localhost:4000" });
await clerk.users.createUser({ emailAddress: ["[email protected]"], password: "supersecret123" });Or run it in-process for tests (no separate server):
import { createServer, clerkPlugin } from "clerk-emulator";
const { app } = createServer(clerkPlugin, {
tokens: { sk_test_emulate: { login: "admin", id: 1, scopes: [] } },
});
const res = await app.request("/v1/users", { headers: { Authorization: "Bearer sk_test_emulate" } });app is a Hono app — call app.request(...) in-memory, or serve({ fetch: app.fetch, port }) (@hono/node-server) to bind a port.
Point the frontend (clerk-js) at it: set a relative proxyUrl so clerk-js sends Frontend API calls to a path your dev server forwards to the emulator (clerk-js forces https on absolute proxy URLs, so it must be relative). Pin clerkJSVersion to skip the floating @<major> CDN redirect.
// React entry
<ClerkProvider publishableKey="pk_test_..." proxyUrl="/__clerk" clerkJSVersion="6.17.0">// vite.config.ts — forward the proxy path to the emulator
server: {
proxy: {
"/__clerk": { target: "http://localhost:4000", changeOrigin: true, rewrite: (p) => p.replace(/^\/__clerk/, "") },
},
}Vanilla clerk-js is the same idea: new Clerk(pk, { proxyUrl: "/__clerk" }). See e2e/ for complete React, @clerk/clerk-react, and vanilla setups.
What's supported
| Capability | Details |
|---|---|
| Users & email addresses | full CRUD, ban / lock, metadata, verify password |
| Organizations | CRUD, memberships, invitations, domains |
| Sessions & tokens | session CRUD, JWT minting, v1/v2 negotiation (clerk-api-version / __clerk_api_version) |
| M2M tokens | create, verify, revoke |
| OAuth 2.0 / OIDC | authorize, token, userinfo, JWKS |
| Frontend API (FAPI) | sign-in, MFA (TOTP), sign-up |
| Webhooks | Clerk-shaped resource events |
| Testing tokens | @clerk/testing compatible — POST /v1/testing_tokens, __clerk_testing_token |
| Out of scope | social sign-in, passkeys, SAML / enterprise SSO, multi-session, billing, profile management |
Compatibility
Verified in CI against:
| Surface | SDK | Versions | Clerk core |
|---|---|---|---|
| Frontend (React) | @clerk/react | latest 6.x | Core 3 (Active) |
| Frontend (React) | @clerk/clerk-react | latest 5.x | Core 2 (LTS) |
| Frontend (vanilla) | @clerk/clerk-js | latest 5.x, 6.x | Core 2 (LTS), Core 3 |
| Backend | @clerk/backend | latest 2.x, 3.x | Core 3 |
| Runtime | Node.js | 20, 22, 24 | — |
Not yet tested: @clerk/backend 0.x (Core 2 LTS, pre-Core-3 API) and @clerk/nextjs.
Tests
npm test # unit + integration (Node, against a real server)
cd e2e && npm test # browser e2e (Playwright, real clerk-js)| Layer | Covers |
|---|---|
| Browser e2e — Playwright + clerk-js | sign-in (password / email-code / MFA-TOTP), sign-up, org listing |
| SDK e2e — @clerk/backend over HTTP | admin lifecycle: users, organizations, memberships, invitations, domains, M2M |
| HTTP integration — raw requests | OAuth / OIDC, webhooks, session-token v1/v2 negotiation |
Browser e2e and the backend SDK run across the version matrix in Compatibility. See docs/testing.md for Playwright/Cypress (@clerk/testing), backend-SDK, and in-process setups.
License & attribution
Apache-2.0. The framework under src/framework/ is adapted from vercel-labs/emulate (Apache-2.0) with its HTTP layer swapped for Hono — see NOTICE.
"Clerk" is a trademark of Clerk, Inc. This is an independent, unofficial tool — not affiliated with, endorsed by, or sponsored by Clerk, Inc.
