Getting started

Getting started

From an OpenAPI spec to a served response, in about a minute.

You need the apifae binary — see Install — and a directory containing an OpenAPI 3.x or Swagger spec. If you want to follow along exactly, download the manual's own fixture, orders.openapi.yaml:

mkdir orders && cd orders
curl -fsSL https://apifae.com/manual/fixtures/orders.openapi.yaml -o openapi.yaml

It is four endpoints and fits on one screen. Working from a clone of the repository, cp docs/manual/fixtures/orders.openapi.yaml ./openapi.yaml is the same file.

1. Initialise the workspace

$ apifae init -N

✓ Project 'orders' initialized successfully!

Created:
   ✓ Created apifae.yaml
   ✓ Created schemas
   ✓ Created mocks
   ✓ Created schemas/openapi.yaml
   ✓ Created mocks/…
   ✓ Created mocks/…
   ✓ Created scenarios/from-spec.yaml

Next steps:
   1. apifae serve # Start mock server
   2. apifae validate # Validate schemas

Happy mocking!
EXIT: 0

init found the spec by itself — no flag, no path argument. It copied it to schemas/ and wrote one mock per documented path — here mocks/orders.yaml and mocks/reports/daily.yaml, one per path group.

-N is the non-interactive form. It imports the spec exactly as the prompt does, and it is what a Dockerfile or a CI step runs.

The mocks are populated, not stubbed. Every property in those files comes from the spec's own example: values, which is why the next step serves something believable rather than {}.

Each declared error response is generated too. The spec documents a 404 on GET /orders/{id}, so a 404 exists — reachable through a scenario rather than by default, since a mock that 404s by default is not much of a mock. See Scenarios.

2. Check it

$ apifae validate
Loading OpenAPI spec from …/openapi.yaml...
  → Orders API v1.0.0
Loading mocks from mocks...
  → 4 endpoints from 2 files

Validating mocks against OpenAPI spec...


4 mocks checked, 4 valid, 0 errors, 0 warnings

✓ All mocks valid
EXIT: 0

validate renders every template and type-checks the result against the spec, so this is a stronger statement than "the YAML parses".

3. Serve it

$ apifae up
✓ APIFae mock server running at http://127.0.0.1:4000
  Loaded 4 endpoints from 2 files
  Watching for changes...

Leave it running. It watches the workspace, so editing a mock is reflected on the next request — no restart.

4. Use it

Every endpoint the spec documents is live, at the status the spec declares:

curl http://127.0.0.1:4000/orders
{"data":[{"createdAt":"2026-08-01T09:15:00Z","currency":"EUR","customerEmail":"ada@example.com","id":"ord_8f14e45fceea","note":"Leave with the concierge","status":"paid","total":4250}],"hasMore":false,"page":1,"total":3}

The four endpoints answer like this:

Request Status
GET /orders 200
GET /orders/ord_42 200
POST /orders 201
GET /reports/daily 500

POST /orders returns 201 and /reports/daily returns 500 because that is what the spec declares. /reports/daily documents no success response at all, so its mock is the 500 — the tool is not inventing a happy path the contract does not describe.

5. Error states, without a second workspace

The 404 from step 1 lives behind a scenario:

$ apifae scenario list
Scenarios:
  getOrder-404
    GET /orders/{id} responds 404 (declared in the OpenAPI spec)
EXIT: 0

Activate it against the running server, and GET /orders/{id} starts returning 404 until you reset:

apifae scenario set getOrder-404
apifae scenario reset

One workspace, several API conditions. See Mock an API before it exists for the states worth building, and Scenario files for the file format.

Your mocks are only as good as your spec's examples. In this fixture the 404 and the 500 share one Error schema, whose example: values read report_failed / "Report worker is not available". So the 404 body says the report worker is unavailable, which is nonsense for a missing order. APIFae copied the spec faithfully; the spec is what is vague. Give an error response its own example: when you want its mock to be specific.

6. Keep it honest

This is the part that makes APIFae different from writing the mocks by hand. Point diff at the real API and it will tell you whether your mocks — and your spec — still describe it:

apifae diff https://api.example.com

That is a guide of its own: Keep a mock honest.

Where things live

orders/
├── apifae.yaml              the workspace config
├── openapi.yaml             the spec — this is the one commands read
├── mocks/                   one file per path group
│   ├── orders.yaml
│   └── reports/daily.yaml
├── scenarios/from-spec.yaml named states
└── schemas/openapi.yaml     where init puts the spec it imported

Where the spec is looked for, in this order: openapi.yaml, openapi.yml or openapi.json at the workspace root, then the schemas: globs in apifae.yaml. validate, up, patch and diff all resolve it the same way, so the copy init writes into schemas/ is found without you moving anything — and while a root spec exists, it is the one that counts.

A schemas/ directory holds shared fragments too; only a file that declares itself with a top-level openapi: or swagger: key is treated as the spec, and when several do, the first in sorted order wins.

It is all text, and it is all meant to be committed. Sharing a workspace with your team means committing it to git — there is no hosted service, and the four commands that name one refuse rather than pretend. See Commands that refuse.

This manual is generated from the repository. Every command and flag in it is checked against the binary; the marked transcripts are executed.