User manual

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.

OpenAPI, formerly Swagger, describes an API’s endpoints, parameters, and responses in YAML or JSON. Use your own spec or try the public Petstore URL below. See how the pieces fit together.

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.

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");
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const pet = await response.json();
console.log(pet);

A successful request that logs a JSON object confirms the local API is responding. Supported operations return schema-derived sample data before you edit a handler. The generated values can vary; a response might look like this:

{
  "id": 1,
  "name": "string",
  "photoUrls": [],
  "status": "available"
}

The sample data matches the response schema. Edit the handler when you need specific values or behavior.

Know what was generated

Counterfact creates two directories:

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.

Customize a response

The handler below uses the response.status(...) API from main, which is not in the published 2.17.0 package. Stop the trial server with Ctrl+C, complete the pinned source setup, and run npx --no-install counterfact https://petstore3.swagger.io/api/v3/openapi.json api from its exercise directory before editing.

When a screen needs a specific case, replace the generated response in the corresponding handler. Counterfact supplies $: $.path contains URL parameters and $.response builds the HTTP response. The imported HTTP_GET type comes from the generated contract:

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

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

Save the file, then repeat the browser request above—no server restart is needed. The logged pet should now have name: "Fluffy". If you pasted the request into a console, use a fresh scope or reload the page before declaring the same variables again.

Use a repeatable project

For a project or CI workflow, install a pinned version and commit the lockfile. First 10 minutes walks through stopping this trial, using a pinned implementation and contract in a separate project, and adding state. Use project installation for automated workflows.

Next steps