OpenAPI to TypeScript generator and mock server

This project is maintained by pmcelhaney

Coverage Status Mutation testing badge MIT License


Counterfact is two complimentary tools in one:

Hello World Pet Store ๐Ÿ‘‹

The easiest way to start is to copy and paste this command into your terminal.

npm i -g ts-node
npx counterfact@latest api --open

It will generate TypeScript code for the Swagger Pet Store, start a server, and --open a browser running Swagger UI. We're using the pet store example because it's well known and convenient. If you have your own OpenAPI document handy, you can point to that instead. You can also change api to wherever you'd like to output the code.

Here are the full details on CLI usage
Usage: counterfact [options] <openapi.yaml> [destination]

Counterfact is a tool for generating a REST API from an OpenAPI document.

openapi.yaml path or URL to OpenAPI document
destination path to generated code (default: ".")

--serve start the server after generating code
--port <number> server port number (default: 3100)
--swagger include swagger-ui (implies --serve)
--open open a browser to swagger-ui (implies --swagger and --serve)
-h, --help display help for command

Generated Code ๐Ÿ–

After generating code you should have three directories:

The code under components and path-types is regenerated every time you run Counterfact, so that the types can stay in sync with any OpenAPI changes. The code under paths is minimal boilerplate that you're meant to edit by hand. Counterfact will not overwrite your changes in the paths directory, but it will add new files when necessary.

You don't have to use the code generator. It wasn't even part of Counterfact originally. You can also create the files under paths by hand. The main benefit of generating code is all the type information that's managed for you and kept in sync with OpenAPI.

Routing is where it's at ๐Ÿ”€

In the paths directory, you should find TypeScript files with code like the following.

export const GET: HTTP_GET = ($) => {
  return $.response[200].random();

export const POST: HTTP_POST = ($) => {
  return $.response[200].random();

The file's path corresponds to the endpoint's URL. Each of the exported functions implements an HTTP request method (GET, POST, PUT, etc.). Each of these functions takes one argument -- $ -- which is used to access request information, build a response, and interact with the server's state.

If you're familiar with Express, $ is sort of a combination of req and res with type safety and extra super powers.

The $.response object

The $.response object is used to build a valid response for the URL and request method. It's designed to work with your IDE's autocomplete feature and help you build a valid response without consulting the docs. Try typing $.response. in your IDE. You should see a list of numbers corresponding to HTTP response codes (200, 404, etc). Select one and then type another .. At this point the IDE should present you with one or more of the following methods.

You can build a response by chaining one or more of these functions, e.g.

return $.response[200].header("x-coolness-level", 10).text("This is cool!")`.

Your IDE can help you build a valid response via autocomplete. It can also help ensure the response matches the requirements in the OpenAPI document. For example, if you leave out a required header, the function won't type check. (That's particularly useful when there are API changes. Update the OpenAPI document, regenerate the types, and let TypeScript tell you where to update the code.)

Request parameters: $.path, $.query, $.headers, and $.body

Most of the time, the server's response depends on input from various parts of the request: the path, the query string, the headers, or the body. We can use them like so:

export const GET: HTTP_GET = ($) => {
    if ($.headers['x-token'] !== 'super-secret') {
       return $.response[401].text('unauthorized');

    const content = `TODO: output the results for "${query.keyword}"`
      + `in ${$.path.groupName}`
      + `that have the following tags: ${$.body.tags.join(',')}.`.

    return $.response[200].text(content);

Note that each of these objects is typed so you can use autocomplete to identify the parameters. For example, if you type $.query. you'll be presented with a list of expected query string parameters.

The $.path parameters are identified by dynamic sections of the path, i.e. /groups/{groupName}/user/{userId}.ts.

Working with state: the $.context object and $.context.ts

There's one more parameter we need to explore, the $.context object. It stands in for microservices, databases, and other entities with which the real API interacts. It looks something like this:

// pet.ts
export const POST: HTTP_POST = ($) => {
  return $.response.json($.context.addPet($.body));
// pet/{id}.ts
 export const GET: HTTP_GET ($) => {
    const pet = $.context.getPetById(body);
    if (pet === undefined) return response[404].text(`Pet ${$} not found.`);
    return response.json(context.getPetById($.body));

The context object is defined in $.context.ts in the same directory as the file that uses it. It's up to you to define the API for a context object. For example, your $.context.ts file might look like this.

class PetStore {
  pets: Pet[] = [];

  addPet(pet: Pet) {
    const id = this.pets.length;
    this.pets.push({, id });
    return this.getPetById(id);

  getPetById(id: number) {
    return this.pets[id];

export default new PetStore();

By default, each $.context.ts delegates to its parent directory, so you can define one context object in the root $.context.ts and use it everywhere.

You can make the context objects do whatever you want, including things like writing to databases. But remember that Counterfact is meant for testing, so holding on to data between sessions is an anti-pattern. Keeping everything in memory also makes it fast.

Reloading is So Hot Right Now ๐Ÿ”ฅ

Changes you make will be picked up by the running server immediately. There's no need to restart!

Hot reloading supports one of Counterfact's key design goals. While developing and testing, we want to explore counterfactuals, such as

In such cases, we want to be sure the front end code responds appropriately. Getting a real server to do what we need to test front end code is usually difficult if not impossible. Counterfact is optimized to make bending the server's behavior to suit a test case as painless as possible, in both manual and automated tests.

REPL without a Pause โฏ

Another way to explore counterfactuals in real time is to interact with the running server via the read-eval-print loop (REPL), in the same way that you interact with running UI code in your browser's developer tools console. If you look in the terminal after starting Counterfact you should see a prompt like this:

Welcome to Counterfact!

Counterfact is a mock server used to develop and test your front end app.
There are several ways to poke and prod the server in order to make it behave the way you need for testing.

1. Call the REST APIs at http://localhost:3100 (with your front end app, curl, Postman, etc.)
2. Change the implementation of the APIs by editing files under /Users/pmcelhaney/code/counterfact/out/paths (no need to restart)
3. Use the GUI at http://localhost:3100
4. Use the REPL below (type .counterfact for more information)


At the > prompt, you can enter JavaScript code to interact with the live context object. For example, here's a quick way to add a pet to the store.

context.addPet({ name: "Fluffy", photoUrls: [] });

Or add 100 pets:

for (i = 0; i < 100; i++) context.addPet({ name: `Pet ${i}`, photoUrls: [] });

Or get a list of pets whose names start with "F"

context.pets.find((pet) =>"F"));

Using the REPL is a lot faster (and more fun) than wrangling config files and SQL and whatever else it takes to a real back end into the states you need to test your UI flows.

Proxy Peek-a-boo ๐Ÿซฃ

At some point you're going to want to test your code against a real server. Or maybe you want to use Counterfact for newer endpoints that don't exist in the real server yet and a real server for everything else. Counterfact has a couple of facilities that allow you to proxy to the real server on a case-by-case basis.

To proxy an individual endpoint, you can use the $.proxy() function.

// pet/{id}.ts
 export const GET: HTTP_GET ($) => {
    return $.proxy("")

To toggle globally between Counterfact and a proxy server, pass --proxy-url <url> in he CLI.

Then type .proxy on / .proxy off in the REPL to turn it on and off. When the global proxy is on, all requests will be sent to the proxy URL instead of the mock implementations in Counterfact.

This feature is hot off the presses and somewhat experimental. We have plans to introduce more granular controls over what gets proxied when, but we want to see how this works first. Please send feedback!

No Cap Recap ๐Ÿงข

Using convention over configuration and automatically generated types, Counterfact allows front-end developers to quickly build fake REST APIs for prototype and testing purposes.

We're Just Getting Started ๐Ÿฃ

More features are coming soon:

Please send feedback / questions to or create a new issue.

And yes, contributions are welcome!