gofri Why two g's? When I was a kid my grandma called me Gofri, and the family picked it up. She said the G hard, almost twice, so I write it twice.

Fake-End: mock APIs with YAML and TypeScript

My contribution
Design and implementation.
Scope
Local HTTP mocks with file-based routing, interpolation, guards and fixtures.
Evidence
Implementation inspected at a2f3290; reconstructed examples are labelled.

Page by Julian Ramirez ·

The interface is ready to send a request. The backend is not ready to answer it. You can hard-code a response inside the component, but that shortcut leaves the request path, loading behavior and failure states largely untested.

Fake-End moves the mock to the HTTP boundary. Local YAML or TypeScript files describe endpoints, letting the frontend keep making requests while the backend contract takes shape. The point is to keep working against an explicit response shape instead of scattering temporary data through the UI.

That becomes especially valuable when the screen has more than a happy path. A useful mock should help you build the empty state, the rejected request and the retry, too.

My contribution and design scope

I built Fake-End to put mock responses at the HTTP boundary. Its implementation connects file-based routes, request interpolation and guarded responses so the frontend can exercise both successful and rejected requests. The constraint is local development: a useful fixture still needs to be reconciled with the real API.

Turn a folder into a small API contract

Fake-End derives routes from the mock file’s location together with its endpoint path. A users.yaml file and a nested api/products.yaml file therefore occupy different places in the local API. The mapping keeps the mock’s structure visible in the project tree.

mock_server/
  users.yaml
  api/
    products.yaml
/users/ /api/products/
Example files · endpoint path: /Illustrative schematic
Sources & implementation notes

Inside the file, the method, path, status and body define the response. In this example, the route accepts a user ID and returns it in a successful response. It is enough to connect a frontend request to something the developer can inspect and edit.

- method: GET
  path: /:id
  status: 200
  body:
    id: ":id"
GET /users/ada
200 OK
{ "id": "ada" }
Illustrative users.yaml → HTTP responseIllustrative schematic
Sources & implementation notes

TypeScript interfaces offer another route to fixtures. Field names and types can inform generated values, giving the UI data with a useful shape. Those generated examples remain development fixtures; realistic-looking values do not make them production evidence.

Let the request shape the response

A static response is useful until every request starts looking identical. Request interpolation lets the mock reflect a path parameter, a body field or a query value without putting that logic back into the frontend.

Use the controls to follow each value from its request to its response. The syntax differs by source: path parameters use :id, while body and query values use placeholders. Keeping that distinction visible makes mistakes in the mock easier to diagnose.

GET /users/ada
id: ":id"
{ "id": "ada" }
body: { "name": "Ada" }
name: "{{body.name}}"
{ "name": "Ada" }
GET /users?page=2
page: "{{query.page}}"
{ "page": "2" }
Illustrative substitutions · request values retain their typesIllustrative schematic
Sources & implementation notes

This is still a contract you control locally. An example response can tell you whether the interface handles a shape; it cannot establish that the eventual backend implements the same behavior. The mock and the real API still need to converge.

Make failure a first-class test case

The next step is to give the endpoint a reason to refuse a request. Fake-End guards evaluate conditions and select a success or failure response. The inspected implementation supports equality, existence and containment checks, including their negative forms.

In the illustrative flow below, an email’s presence determines whether the UI can show a successful creation or needs to ask the user for more information. Both outcomes come from the same endpoint, so the frontend exercises its response handling rather than a separate demo-only branch.

POST /users
{}
400 · left branch
Add an email to continue.
POST /users
{ "email": "ada@example.com" }
201 · right branch
User created ✓
Illustrative UI states from the same guardIllustrative schematic
Sources & implementation notes

That is the practical payoff: a mock can expose unfinished product behavior before the real service is available. The failure state stops being something to “add later.”

Choose predictability, variation and reload deliberately

Static fixtures help when you want to revisit the same screen state. Dynamic mock functions can be evaluated on each request with --dynamic-mocks, which is useful when variation is part of the test. Dynamic does not mean guaranteed uniqueness, and static generated values need not repeat across server starts.

Static startup → value A A → A → A
Dynamic functions each request → evaluate A → B → C
Illustrative values · dynamic does not guarantee uniquenessIllustrative schematic
Sources & implementation notes

--dynamic-mocks executes mock functions on each request instead of at startup. Static generated values can differ across server starts; they are not seeded deterministically by this example.

Reloading is also an explicit choice at this revision: run fake-end run --watch. The README describes hot reload broadly, but the implementation enables its watchers only when that flag is set.

I would begin with one endpoint and two meaningful outcomes. Once both work through the real frontend request path, expand the contract. For another example of keeping structure visible before execution, see how Ohtools exposes tool contracts.

Explore Fake-End’s local API mocks →