Skip to content

Latest commit

 

History

History
149 lines (109 loc) · 4.85 KB

File metadata and controls

149 lines (109 loc) · 4.85 KB

REPL ⬣

The REPL is a JavaScript prompt connected directly to the running server — like the browser DevTools console, but for your mock API. After starting Counterfact you'll see:

____ ____ _  _ _ _ ___ ____ ____ ____ ____ ____ ___
|___ [__] |__| |\|  |  |=== |--< |--- |--| |___  |
            Storybook for the back-end

| API Base URL  ==> http://localhost:3100
| Admin Console ==> http://localhost:3100/counterfact/

⬣>

At the prompt you can interact with the live context:

// add a single pet
context.addPet({ name: "Fluffy", photoUrls: [] });

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

// query state
context.pets.filter((pet) => pet.name.startsWith("F"));

To access context from a subdirectory:

const petsContext = loadContext("/pets");

The built-in client object lets you make HTTP requests from the prompt without leaving the terminal:

client.get("/users");
client.post("/users", { name: "bob" });
client.put("/users/1", { name: "robert" }, { "x-api-version": "2" });

All standard HTTP methods are supported. Arguments are: path, body (where applicable), headers.

The built-in route() function creates a fluent request builder that validates required parameters against your OpenAPI document before sending:

// Build and inspect before sending
const req = route("/pet/{petId}").method("get").path({ petId: 42 })
req.ready()    // true / false
req.missing()  // lists missing required parameters
req.help()     // prints OpenAPI docs for the operation
await req.send()

See the Route Builder guide for full documentation.

Scenario scripts with .scenario

For more complex setups you can automate REPL interactions by writing scenario scripts — plain TypeScript files that export named functions. Run them with .scenario:

⬣> .scenario soldPets

Path resolution: the argument to .scenario is a slash-separated path. The last segment is the function name; everything before it is the file path, resolved relative to <basePath>/scenarios/ (with index.ts as the default file).

Command File Function
.scenario soldPets scenarios/index.ts soldPets
.scenario pets/resetAll scenarios/pets.ts resetAll
.scenario pets/orders/pending scenarios/pets/orders.ts pending

A scenario function receives a single argument with { context, loadContext, routes, route }:

// scenarios/index.ts
import type { Scenario } from "../types/scenario-context.js";

export const soldPets: Scenario = ($) => {
  // Mutate context directly — same as typing in the REPL
  $.context.petService.reset();
  $.context.petService.addPet({ id: 1, status: "sold" });
  $.context.petService.addPet({ id: 2, status: "available" });

  // Store a pre-configured route builder for later use in the REPL
  $.routes.findSold = $
    .route("/pet/findByStatus")
    .method("get")
    .query({ status: "sold" });
}

After the command runs you can immediately use anything stored in $.routes:

> routes.findSold.send()

The Scenario type and ApplyContext interface are generated automatically into types/scenario-context.ts when you run Counterfact with type generation enabled.

Startup scenario

The startup export in scenarios/index.ts is special: it runs automatically when the server initializes, right before the REPL prompt appears. Use it to seed dummy data so the server is immediately useful without any manual REPL commands.

// scenarios/index.ts
import type { Scenario } from "../types/scenario-context.js";

export const startup: Scenario = ($) => {
  $.context.addPet({ name: "Fluffy", status: "available", photoUrls: [] });
  $.context.addPet({ name: "Rex", status: "sold", photoUrls: [] });
};

Delegating to other scenario functions keeps startup focused and readable. Pass $ (and any extra arguments) to each helper:

// scenarios/index.ts
import type { Scenario } from "../types/scenario-context.js";
import { addPets } from "./pets.js";
import { addOrders } from "./orders.js";

export const startup: Scenario = ($) => {
  addPets($, 20, "dog");
  addOrders($, 5);
};
// scenarios/pets.ts
import type { ApplyContext } from "../types/scenario-context.js";

export function addPets($: ApplyContext, count: number, species: string) {
  for (let i = 0; i < count; i++) {
    $.context.addPet({ name: `${species} ${i + 1}`, status: "available", photoUrls: [] });
  }
}

If startup is not exported from scenarios/index.ts, it is silently skipped — no error is thrown.

See also