laqi Install

laqi · contract-first API mocks

local by default · no account · JSON in your repo

Build the frontend.
Before the backend is ready.

  1. 01Agree on the API contract.
  2. 02laqi serves it immediately.
  3. 03 Build against realistic responses. successemptyslow401500
  4. 04Switch to the real backend by changing one base URL.
Watch it change See how it works

MIT · Node 20+ · source on GitHub

Flip a response in the panel — the app’s next request gets it. No reload, no restart.

why it works

The team was ready. The API wasn’t.

Agree on the contract. Build everything else in parallel.

Define the API contract once. laqi serves it immediately while the backend builds the real implementation.

born on a frontend team running 2–3 sprints ahead of its backend

before Implementation becomes the dependency. The frontend can’t integrate until the backend exists.

frontend

Ready to build

fetch("/todos") → ?

backend

Still in development

sprint 3 of 5

after One contract. Two teams moving at once. laqi serves the agreed API today. The backend implements the same contract independently.

The contract is plain TypeScript — the one language both teams share. laqi turns it into a working API today; the backend implements it.

frontend + backend agree on it together

TS api contract todo.ts
export interface Todo {
  id: string;
  title: string;
  done: boolean;
}

plain TypeScript · common language

laqi reads the contract
and simulates every endpoint

frontend

Builds today

2–3 sprints ahead

laqi

Serves the contract

temporary · every state

backend

Builds it for real

in parallel

switch when
it’s ready

change base URL
+ run the same tests

real API

Contract implemented

same routes · real data

No integration rewrite. Just point to the real API.

  1. before implementation

    Agree on the contract

    Frontend and backend define the routes, types and states together.

  2. while building

    Build in parallel

    laqi serves the contract now. The backend implements the same contract for real.

  3. when ready

    Switch, then verify

    Change the base URL and run the same flows against the real API.

The real gain was never the mock server.
It was removing a dependency from delivery.

what the frontend track gets 01 a contract 02 types & data 03 every state 04 the reason why 05 a real device

01 / 05 contract · plain JSON

Agree the API once.
Then both sides build against it.

Routes are defined once. Each named response is a state that matters. The file lives in git — the frontend serves it with laqi today, the backend implements the same interface.

the shift

The contract becomes the dependency — not the backend implementation.

named responseslives in gitimport OpenAPI

laqi/api.json saved 2s ago
1 {
2   "GET /todos": { route · defined once
3     "default": "ok",
4     "responses": { named states
5       "ok":    { "body": [{ "id": 1 }] },
6       "empty": { "body": [] },
7       "slow":  { "delay": 2400 },
8       "error": { "status": 500 } ← a state that matters
9     }
10   }
11 }

frontend

laqi start → served now

backend

implements the same routes

02 / 05 model · from types to responses

Start from your types.
laqi writes the responses.

Paste a TypeScript interface, point at a schema already in your project, or drop in sample JSON. laqi generates realistic data that fits it — and goes the other way too, giving you types for any response.

why it matters

The mock and the frontend’s types can’t drift apart — they come from the same model.

from a modelfrom JSONfrom a schema in the projectcopy types

GETPOSTPUT /orders/:id ok 200
blankfrom a modelfrom JSONfrom a schema in the project

your model

1 export interface Order {

2   id: string;

3   total: number;

4   status: 'paid' | 'pending';

5   createdAt: Date;

6 }

generated · ok 200

1 {

2   "id": "ord_8f2a41",

3   "total": 148.5,

4   "status": "paid",

5   "createdAt": "2026-08-24T14:32:07Z"

6 }

examples simplemediumcomplex Create

03 / 05 flip · every edge case, on demand

What if it fails?
Find out before the backend can tell you.

Test the states you usually have to wait for — or can never get on purpose. Pick one, and your app’s next ordinary request gets it. Nothing restarts, and your app does not know the difference.

the point

The error path stops being the one nobody ever sees until a user does.

500 error 401 unauthorized 422 validation empty list slow · 3soffline scenario

laqi watching examples/todo-app/laqi endpoints 5 local 127.0.0.1:8000 overridden 1
scenarios logged-out offline 3 slow-network
filter by method, path, description or response name  (/) 5 shown + New endpoint
GET /todos The whole list ok 200 empty 200 error 500 scenario
POST /todos Create a todo created 201 error 500 scenario
GET /profile The signed-in user ok 200 unauthorized 401 default
GET /health Liveness ok 200 error 500 state
POST /session Sign in ok 200 invalid 401 default

04 / 05 watch · resolution, explained

Every request tells you why.
No guessing which rule won.

Each line shows what came back and what chose it — a header on one request, a state you picked in the panel, the active scenario, or what the file says. Requests that matched no route show up too. That one is usually the bug.

same answer, three places

The panel, the terminal and the x-laqi-resolved header always say the same thing.

header state scenario default

requests 218 requests
09:15:25 GET /todos 200 state → empty 3ms
09:15:28 POST /todos 500 scenario → error 2ms
09:15:31 GET /todos 200 header → slow 2.4s
09:15:33 GET /profile 401 state → unauthorized 1ms
09:15:36 PATCH /todos/12 200 default → ok 1ms
09:15:38 GET /favicon.ico 404 no matching route 0ms

05 / 05 share · a public URL

Localhost, anywhere.
Test on a real device. Deploy nothing.

laqi --share opens a temporary public URL through a Cloudflare tunnel, so a phone, a tester or a teammate on another network reaches the same mocks — and every request still shows up in your own log. Only the mock responses travel: the panel and its write API are not mounted on that listener at all.

without it

You would be deploying a throwaway backend just so a phone could reach it.

explicittemporarytoken-protectedoff by default

zsh — laqi

⚡ laqi 2.0.1 ready in 22ms

serving http://127.0.0.1:8000

panel http://127.0.0.1:8000/__laqi local only

watching examples/todo-app/laqi/ 7 endpoints · 21 responses · 4 scenarios

public https://cold-relay-7f2a.trycloudflare.com

token lq_7f2a41c8d93b 240 req/min

09:22:04 GET /todos 200 via public

09:22:06 POST /todos 500 via public

just files

Your mock API is just files. Yours to keep.

They live alongside your code. Keep them local for yourself, or commit them when the team should share them — clone the repo and everyone gets the same contract, data and scenarios. laqi reads and writes the files you already own; it never becomes the source of truth.

  • Plain files in your project. Keep them local or commit them.
  • Git-native when you want it. Review, diff, branch, clone.
  • No hosted project. No account, no cloud workspace, no remote database.
  • Runtime state is disposable. Your contract and data are what persist.
your repo todo-app main

▾ laqi/ · your mock API

▢ api.json 7 routes · 19 responses

▢ scenarios.json 4 scenarios

▸ src/

laqi/api.json · "POST /todos"

+ "validation": { "status": 422, "body": {…} },

− "error": { "status": 500 }

local

Keep it local

use privately · nothing to set up

git

Commit when it becomes team knowledge

review · diff · branch

team

Everyone gets the same mock API

same contract · data · scenarios

disposable

selected responsetemporary flipslocal overridespanel state

never committed

the name

A runner, ahead of the road.

The chasqui was a relay runner of the Inca road network, carrying messages between waystations across the Andes.

laqi plays the same role for your team. It carries the agreed API contract before the real backend implementation exists.

laqi carries the contract before the real API arrives.

roots

Quechua — llulla, false · chasqui, messenger

say it out loud: it sounds like “lucky”

Stop waiting for the backend.
Start with the contract.

or run it without installing · then laqi init, five questions, every one skippable with enter

MIT · Node 20+ · Quick start

Plain JSON in your repo · runs on your machine · --share exposes only mock responses, never the control plane.