You want to automate repetitive REPL interactions — seeding data, configuring state, building reusable request sequences — so you don't have to type the same commands every session.
The REPL is powerful for ad-hoc exploration, but many setup tasks are predictable: seed a set of pets before a demo, reset state to a known baseline for a test, or configure a reusable route builder for a common request. Retyping these commands every time the server restarts is tedious and error-prone.
Write scenario scripts — TypeScript files in the scenarios/ directory that export named functions. Each function receives a single $ argument that exposes the full live context and route builder. Run any export on demand from the REPL with .scenario, or have one run automatically at startup.
Scenario functions are typed with the generated Scenario type:
// scenarios/index.ts
import type { Scenario } from "../types/scenario-context.js";
export const soldPets: Scenario = ($) => {
// Mutate context directly — the same object route handlers see as $.context
$.context.petService.reset();
$.context.petService.addPet({ id: 1, status: "sold" });
$.context.petService.addPet({ id: 2, status: "available" });
// Store a pre-configured route builder in the REPL environment
$.routes.findSold = $
.route("/pet/findByStatus")
.method("get")
.query({ status: "sold" });
};Use the .scenario command. The argument is a slash-separated path where the last segment is the function name and everything before it is the file path relative to scenarios/ (with index.ts as the default):
⬣> .scenario soldPets
Applied soldPets
| 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 |
After running, anything stored in $.routes is immediately available in the REPL:
⬣> routes.findSold.send()Export a function named startup from scenarios/index.ts and Counterfact calls it automatically when the server initializes, right before the REPL prompt appears — no manual command required:
// 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: [] });
};If startup is not exported, the server starts normally with no error.
Split large scenarios into focused helper functions in separate files and pass $ (and any extra arguments) through. This keeps each file small and lets you call the same helpers from both startup and the REPL with different parameters:
// 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"); // seed 20 dogs at startup
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: [],
});
}
}// scenarios/orders.ts
import type { ApplyContext } from "../types/scenario-context.js";
export function addOrders($: ApplyContext, count: number) {
for (let i = 0; i < count; i++) {
$.context.addOrder({ petId: i + 1, quantity: 1, status: "placed" });
}
}Helpers can also be called as standalone .scenario commands:
⬣> .scenario pets/addPets
- Scenario functions are plain TypeScript — no special framework, fully type-checked, easy to test in isolation.
.scenarioprovides on-demand state changes without editing handler files or restarting the server.startupgives the server a deterministic initial state on every restart, eliminating manual seeding steps.- Helper functions accepting extra arguments let the same logic be used for both realistic production volumes (at startup) and minimal data (in the REPL during debugging).
- Scenarios live alongside your handler code, making them easy to discover and keep in sync with the API.
- Mock APIs with Dummy Data — the full range of approaches for populating server responses
- Live Server Inspection with the REPL — interactive exploration and state manipulation at runtime
- Federated Context Files — organize stateful logic that scenarios can seed
- Simulate Failures and Edge Cases — use scenarios to flip failure flags on demand