API requests are most useful when they change with the API. Store them as plain .http
files in the same Git repository, and an endpoint change can update its runnable example in
the same pull request. Reviewers see a normal text diff; new developers get requests that
match the branch they checked out.
The setup does not need a separate request repository or a generated collection. It needs one agreed location, readable names, and a strict rule for secrets.
Pick one predictable location
For a small service, a root-level folder is easy to find:
orders-service/
├── src/
├── tests/
├── http/
│ ├── health.http
│ ├── orders.http
│ └── auth.http
├── .env.example
└── .gitignore
For a modular monolith, files beside the owning feature can work better. The exact location
matters less than consistency. Add one sentence to the README — “runnable API examples live
in /http” — so nobody has to search for the convention.
Group requests by API area
Use one file per coherent surface, not one file per request. Name each request with a ###
separator so the file reads like a small table of contents:
@baseUrl = http://localhost:5080
### Create an order
POST {{baseUrl}}/api/orders
Content-Type: application/json
{ "sku": "KRV-1", "quantity": 2 }
### Get an order
GET {{baseUrl}}/api/orders/1042
Accept: application/json
Keep representative payloads small. A request file is an executable example, not a dump of production data.
Separate safe defaults from secrets
Committed request files should reference values, not contain credentials:
GET {{baseUrl}}/api/orders
Authorization: Bearer {{token}}
Commit a template that documents the required keys:
# .env.example
baseUrl=http://localhost:5080
token=replace-me
Each developer copies it to a local file that Git ignores:
.env
.env.*.local
Before adopting those patterns, check whether the repository already tracks an environment
file whose name matches them; a later .gitignore rule does not remove a file that is
already tracked. Never put a production token into an example, even temporarily — Git
history outlives the cleanup commit.
Karve registers plain dotenv files as environments. The active file supplies
{{variables}} when a request runs, while an @variable declared inside the .http file
wins when both define the same name. Read
variables and environments in .http files for the
client and ecosystem details.
Review request files like code
A pull request should answer four questions:
- Does the example target the endpoint introduced or changed here?
- Is the payload minimal, valid, and free of customer data?
- Are auth values variables rather than secrets?
- Can another developer run it against a safe local or development environment?
If the request documents a bug, include only the smallest payload that reproduces it. If it
describes a destructive operation, name that risk in the ### label and default its host to
localhost or a disposable environment.
Keep requests with the service that owns them
A central “all API requests” repository looks tidy at first, but it creates a synchronization problem: code changes in one repo while its request example waits for a separate commit in another. Keeping the file with the service lets one pull request change both.
For work across many services, add a view over those repositories instead of moving their
files. Karve gathers .http and .rest files from different folders into local collections,
while every source file stays in its original Git checkout. The longer guide to that choice
is organizing .http files across multiple repos.
Use the .http file validator before committing unfamiliar files, and start from the request recipe library when you need common auth, ASP.NET Core, pagination, or environment patterns.