.http files
Every request is a separate file with the .http extension. The layout follows plain HTTP:
# Create a user. Comments start with # or //.POST {{base}}/usersAuthorization: Bearer {{token}}Content-Type: application/json
{ "name": "Viktor", "role": "admin"}
> save user_id = body.id> assert status == 201- Comments — lines starting with
#or//, before the request line or between headers. - Request line —
METHOD URL. The method may be omitted (then it isGET); a trailingHTTP/1.1is accepted and ignored. Spaces inside the URL must be encoded. - Headers —
Name: value, one per line, until the first empty line. - Body — everything after the empty line. Empty lines inside the body are kept.
- Directives — lines starting with
>at the very end of the file:> saveand> assert. See Chaining requests and assertions.
{{name}} placeholders work in the URL, header names and values, and the body — see
Variables and environments.
Details
Section titled “Details”- If the body starts with
{or[and there is noContent-Typeheader, Outry addsContent-Type: application/json. - After substitution the URL must be absolute (
http://orhttps://). A relative URL almost always means a missing{{base}}. - Windows line endings (CRLF) are fine.
- Every request has a 30 second timeout (
outry run --timeoutchanges it) and theUser-Agent: outry/<version>header unless you set your own.
Organizing files
Section titled “Organizing files”Folders are just folders. A typical layout groups requests by resource:
api/├── env.toml├── auth/│ ├── 1-login.http│ └── 2-refresh.http└── users/ ├── create.http ├── get.http └── list.httpoutry run api runs files in alphabetical order of their paths, so numeric prefixes are a simple way
to order a chain. Hidden folders and node_modules, target, vendor, dist are skipped.
Converting to .outry
Section titled “Converting to .outry”outry convert api/ --dry-run # print the resultoutry convert api/ # write a .outry next to every .httpoutry convert api/ --rm # … and delete the .http files# LoginPOST {{base}}/auth/loginX-Request-Id: {{$uuid}}
{"email": "{{email}}", "password": "{{password}}", "remember": true}
> save token = body.access_token> assert status == 200> assert body.user.id existsLogin: POST /auth/login { headers { X-Request-Id: uuid() } body { email, password, remember: true }
expect { status == 200 body.user.id != null }
save token = body.access_token}- The first comment line becomes the request’s name (
Login: POST …) if it is short (up to four words); otherwise the name is made from the file name and the comment becomes the description. {{base}}/…becomes a path,{{x}}in it a parameter{x}; other URLs stay as they are.- A JSON body stays JSON:
"{{x}}"becomes the variablex,{{x}}outside quotes becomesnumber(x). Other bodies become strings. > assertturns intoexpect;existsinto!= null,containsinto.contains()..outrycompares without type coercion, so a header compared with a number is wrapped innumber().{{$uuid}},{{$timestamp}}and{{$randomInt 1 10}}becomeuuid(),now()andrandomInt(1, 9)— the upper bound is inclusive in.outry.
An existing .outry file is never overwritten. The full mapping is in the
format reference.