Counterfact

Build the frontend. Don’t wait for the backend.

Counterfact turns an OpenAPI document into a useful local API in one command. Use it when your frontend is ready but the backend is incomplete, unavailable, unstable, or owned by another team.

Prerequisite

Use Node.js 22 or newer. Counterfact supports Swagger 2.0 and OpenAPI 3.0, 3.1, and 3.2.

Run one command

Give Counterfact an OpenAPI document and an output directory:

npx counterfact@latest https://petstore3.swagger.io/api/v3/openapi.json api

Counterfact writes editable route files and generated TypeScript types to api/, then starts a local server at http://localhost:3100.

For a project or CI workflow, install a pinned version and commit the lockfile:

npm install --save-dev counterfact@2.16.3
npx counterfact ./openapi.yaml api

Make a browser request

Point your frontend at the local base URL. For the Petstore document above, a minimal request looks like this:

const response = await fetch("http://localhost:3100/pet/1");
const pet = await response.json();

Supported operations return schema-derived sample data immediately—before you edit a handler. A response might look like this:

{
  "id": 1,
  "name": "string",
  "status": "available"
}

The generated response is a useful contract-shaped starting point for frontend work and exploration. It is not a substitute for the business behavior you choose to model.

Know what was generated

The output directory contains files you can own:

api/
├── routes/   # editable handlers, one path at a time
└── types/    # generated request and response contracts

Do not edit types/; regenerate them when the OpenAPI document changes. Edit handlers under routes/. Counterfact hot-reloads route changes while the server is running.

Make one response explicit (optional)

When a screen needs a specific case, replace the generated response in the corresponding handler:

// api/routes/pet/{petId}.ts
import type { HTTP_GET } from "../../types/paths/pet/{petId}.types.js";

export const GET: HTTP_GET = ($) =>
  $.response[200].json({
    id: $.path.petId,
    name: "Fluffy",
    status: "available",
  });

Save the file and refresh the frontend—no server restart is needed.

Next steps