Skip to content

HTTP File Format

t-req parses .http files — a human-readable format for defining HTTP requests. This reference covers the complete syntax.

Every request starts with a method and URL:

GET https://api.example.com/users

The HTTP version is optional:

GET https://api.example.com/users HTTP/1.1

Supported methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT.

Use ### to separate multiple requests in a single file:

GET https://api.example.com/users
###
POST https://api.example.com/users
Content-Type: application/json
{"name": "Alice"}

Add a name after the separator to identify the request:

### Get all users
GET https://api.example.com/users
### Create user
POST https://api.example.com/users
Content-Type: application/json
{"name": "Alice"}

Run a named request with the CLI:

Terminal window
treq run api.http --name "Create user"

Lines starting with # or // are comments (before the request line):

# This is a comment
// This is also a comment
GET https://api.example.com/users

Headers follow the request line, one per line in Name: Value format:

GET https://api.example.com/users
Authorization: Bearer my-token
Accept: application/json
Content-Type: application/json

Header names are case-insensitive per HTTP spec.

Separate the body from headers with a blank line:

POST https://api.example.com/users
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com"
}

The body continues until the end of the request block (next ### or end of file).

Load the body from an external file with < ./path:

POST https://api.example.com/users
Content-Type: application/json
< ./fixtures/user.json

Paths are relative to the .http file location.

Use {{variableName}} to interpolate variables:

GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}

Variables come from:

  • treq.jsonc configuration
  • Profile-specific overrides
  • CLI --var flags
  • Programmatic client.setVariable()

Access nested object properties with dot notation:

GET {{baseUrl}}/users
X-User-Id: {{user.id}}
X-User-Name: {{user.profile.name}}

Resolvers provide dynamic values using {{$name()}} syntax:

POST {{baseUrl}}/events
Content-Type: application/json
{
"timestamp": "{{$timestamp()}}",
"secret": "{{$secret(API_KEY)}}"
}

Configure resolvers in treq.jsonc:

{
"resolvers": {
"timestamp": {
"type": "command",
"command": "date +%s"
},
"uuid": {
"type": "command",
"command": "uuidgen"
}
}
}

Pass arguments to resolvers:

{{$random(0, 100)}}
{{$env(API_KEY)}}
{{$secret(DATABASE_PASSWORD)}}

Arguments can include variable references:

{{$secret({{secretName}})}}

Use # @directive value comments to add metadata:

# @name create-user
# @description Creates a new user account
# @timeout 5000
POST {{baseUrl}}/users
Content-Type: application/json
{"name": "Alice"}
DirectiveDescription
@nameRequest name (alternative to ### Name syntax)
@descriptionHuman-readable description
@timeoutRequest timeout in milliseconds
@assertInline assertion expression (via @t-req/plugin-assert)
@sseMark request as Server-Sent Events stream
@lastEventIdResume from a specific event ID

Meta directives must appear before the request line.

With the assertion plugin enabled, add one or more @assert directives before the request line:

# @assert status == 200
# @assert header Content-Type contains application/json
# @assert body contains "token"
# @assert jsonpath $.token exists
GET https://api.example.com/auth/login
Accept: application/json

Supported assertion targets:

TargetOperatorsExample
status== != > >= < <=# @assert status == 200
header <name>exists == != contains# @assert header X-Trace-Id exists
bodycontains not-contains# @assert body not-contains "error"
jsonpath <expr>exists == !=# @assert jsonpath $.count == 2

Invalid or failing assertions cause treq run to exit with code 1.

Mark a request as an SSE stream with the @sse directive:

# @sse
GET https://api.example.com/events/stream
Authorization: Bearer {{token}}

SSE is also auto-detected from the Accept header:

GET https://api.example.com/events/stream
Accept: text/event-stream
DirectiveDescription
@sseMark request as SSE (enables streaming response)
@timeoutConnection timeout in ms (default: 30000)
@lastEventIdResume from event ID (sets Last-Event-ID header)
# @name stockPrices
# @sse
# @timeout 60000
# @lastEventId event-42
GET https://api.example.com/prices/stream
Authorization: Bearer {{token}}

WebSocket request blocks define connection metadata only. Message interaction is runtime-driven through API/SDK clients.

# @ws
# @ws-subprotocols graphql-ws,json
# @ws-connect-timeout 30000
GET wss://api.example.com/graphql
Authorization: Bearer {{token}}

WebSocket is detected by:

  • @ws directive, or
  • ws:// / wss:// URL scheme
DirectiveDescription
@wsMark request as WebSocket
@ws-subprotocolsComma-separated requested subprotocols
@ws-connect-timeoutConnect timeout in milliseconds

WebSocket definitions cannot include:

  • inline request body
  • body file references (< ./file)
  • form-data blocks

The parser still captures raw blocks, but server execution (POST /execute/ws) rejects these with validation errors in protocol 1.1.

t-req supports a friendly form syntax with name = value on separate lines:

POST {{baseUrl}}/login
Content-Type: application/x-www-form-urlencoded
username = alice
password = secret123

This is automatically detected when all body lines match the name = value pattern.

Upload files in form data with @./path:

POST {{baseUrl}}/upload
Content-Type: multipart/form-data
title = My Document
file = @./documents/report.pdf

Specify a custom filename with | filename:

POST {{baseUrl}}/upload
Content-Type: multipart/form-data
document = @./report.pdf | annual-report-2024.pdf

File paths can include variables:

POST {{baseUrl}}/upload
Content-Type: multipart/form-data
file = @{{uploadPath}}

Include query parameters directly in the URL:

GET {{baseUrl}}/users?page=1&limit=10&sort=name

Or use variables:

GET {{baseUrl}}/users?page={{page}}&limit={{limit}}

URL fragments are preserved:

GET {{baseUrl}}/docs#section-2

Specify custom ports:

GET http://localhost:3000/api/users
GET https://api.example.com:8443/secure

Include credentials in the URL (not recommended for production):

GET https://user:password@api.example.com/private

IPv6 addresses use bracket notation:

GET http://[::1]:3000/api/users
GET http://[2001:db8::1]/resource
# User API requests
# These requests demonstrate the full .http syntax
### List users
# @description Get paginated list of users
GET {{baseUrl}}/users?page={{page}}&limit=10
Authorization: Bearer {{token}}
Accept: application/json
### Get user by ID
GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}
### Create user
# @name create-user
# @timeout 5000
POST {{baseUrl}}/users
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "{{user.name}}",
"email": "{{user.email}}",
"createdAt": "{{$timestamp()}}"
}
### Upload avatar
POST {{baseUrl}}/users/{{userId}}/avatar
Authorization: Bearer {{token}}
Content-Type: multipart/form-data
avatar = @./fixtures/avatar.png | profile.png
### Delete user
DELETE {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}
### Stream events
# @sse
# @timeout 60000
GET {{baseUrl}}/events/stream
Authorization: Bearer {{token}}