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
- Try the guided coffee shop — learn by changing real handlers beside a working storefront; no document required.
- First 10 minutes — create a pet, fetch it, and test an error response.
- Find your workflow — find a guide for what you’re working on.
- Custom responses, state, failures, and proxying — add capabilities only when the workflow needs them.
- Without OpenAPI — write routes without an OpenAPI spec.