A file format specification for defining HTTP requests, response assertions, and configuration in "request files".
- HTTP request and response messages
- Easy to read, write, and diff
- Lives in source control
- Environments
- Templating with variables, prompted, and secret values
- Client/implementation agnostic
- Chaining requests
- Response body mapping/transformation/extraction
- Authenticated requests (e.g. OAuth2) configuration
- Project workspaces
Request files (*.reqlang
) are multi-document files containing a request along with an optional config and expected response. They are designed to define what the request is, not how to execute it (e.g. defining what secrets are needed instead of how to fetch them). This is left to implementing clients.
This is a living syntax subject to change wildly at anytime. The core concepts and goals will remain the same however.
```%config
vars = ["test_value"]
secrets = ["super_secret_value"]
[prompts]
prompt_value = ""
[envs.test]
test_value = "test_value"
[envs.prod]
test_value = "prod_value"
[envs.local]
test_value = "local_value"
```
```%request
POST https://httpbin.org/post HTTP/1.1
{
"env": "{{@env}}",
"value": "{{:test_value}}",
"prompted_value": "{{?prompt_value}}",
"secret_value": "{{!super_secret_value}}"
}
```
The request is the request is what's executed when the request file is ran. They are written as HTTP request messages.
```%request
GET https://example.com HTTP/1.1
```
The response is optional but treated as an assertion if it is defined. When the request is executed, this response is compared to the actual response received.
```%request
GET https://example.com HTTP/1.1
```
```%response
HTTP/1.1 200 OK
```
Client implementations can choose how to match the response against the expected response. Here are a list of recommended ways to match.
- Exact match
status code
- Exact match
status text
- Exact match
header value
of headers present in the expected response - Exact match
body
- Wildcard match
header value
andbody
using the{{*}}
template references.
The configuration is optional but is used to define environment names and their variables as well as what prompts & secrets are needed. It currently uses the toml
syntax.
Variables contain environmental variables that can be used in the request or response. A list of variable names is first declared.
Variables can be templated using the {{:var_name}}
syntax. The environment of the request execution can be referenced using the {{@env}}
syntax.
vars = ["user_id", "item_id"]
Then enviroments are declared with the appropriate values.
vars = ["user_id", "item_id"]
[envs.dev]
user_id = 12345
item_id = "abcd"
[envs.prod]
user_id = 67890
item_id = "efgh"
There is an implicitly defined default
environment present but it still must be declared in the config.
vars = ["user_id"]
[envs.default]
user_id = 12345
```%config
vars = ["user_id", "item_id"]
[envs.dev]
user_id = 12345
item_id = "abcd"
[envs.prod]
user_id = 67890
item_id = "efgh"
```
```%request
GET https://{{@env}}.example.com/users/{{:user_id}}/items/{{:item_id}} HTTP/1.1
```
- Clearly define everything the request and response will need
- Declare environments once
- Require variable declaration before definition
- Default value (implicitly set in the
default
environment) - Value type
Prompts are values provided by the user at request execution time. These are "inputs" to the request file. They can be templated in the request and responses using the {{?prompt_name}}
syntax.
[prompts]
tags = ""
```%config
[prompts]
tags = ""
```
```%request
GET https://example.com/posts?tags={{?tags}} HTTP/1.1
```
- Default value
- Value type
Secrets are protected values referenced by a name and declares what secrets will be required. How secret values are fetched is up to client implementations. They can be referenced using the {{!secret_name}}
syntax.
secrets = ["api_key"]
```%config
secrets = ["api_key"]
```
```%request
GET https://example.com HTTP/1.1
x-api-key: {{!api_key}}
```
- Secret fetching is outside the scope of the request file
- Configuring secret fetching in the workspace
See all examples for more request files.
The reqlang crate is a library working with request files.
These act as both tooling for request file and reference implementations for clients.
The reqlang
CLI validates and exports requests in to a variety of formats (http
, curl
, json
).
reqlang
Command to work with request files
Usage: reqlang [COMMAND]
Commands:
export Export request to specified format
parse Parse a request file
run Run a request file
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
Execute the request from a request file.
Usage: reqlang run [OPTIONS] <path>
Arguments:
<path> Path to request file
Options:
-e, --env <env> Resolve with an environment
-P, --prompt <prompts> Input a prompt value
-S, --secret <secrets> Input a secret value
-f, --format <format> Format the response [default: http] [possible values: http, json]
-t, --test Test if the response matches the expected response, if defined
-h, --help Print help
reqlang run ./examples/valid/status_code.reqlang --prompt status_code=200
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
connection: keep-alive
content-length: 0
server: gunicorn/19.9.0
access-control-allow-credentials: true
access-control-allow-origin: *
reqlang run examples/valid/mismatch_response.reqlang --test
See: mismatch_response.reqlang
Response did not match expected response: [
StatusCode {
expected: HttpStatusCode(
200,
),
actual: HttpStatusCode(
201,
),
},
StatusText {
expected: "OK",
actual: "Created",
},
]
Validate and parse request files. It returns a JSON object with info about the request file: environment names, variables, prompts, secrets, the (untemplated) request itself.
Usage: reqlang parse <path>
Arguments:
<path> Path to request file
Options:
-h, --help Print help
reqlang parse ./examples/valid/status_code.reqlang
{
"vars": ["test_value"],
"envs": ["prod", "test", "local"],
"prompts": ["prompt_value"],
"secrets": ["super_secret_value"],
"request": {
"verb": "POST",
"target": "https://httpbin.org/post",
"http_version": "1.1",
"headers": [],
"body": "{\n \"env\": \"{{@env}}\",\n \"value\": \"{{:test_value}}\",\n \"prompted_value\": \"{{?prompt_value}}\",\n \"secret_value\": \"{{!super_secret_value}}\"\n}\n\n"
}
}
Use tools like jq
to extract specific information from the parsed request.
Let a list of environment names defined in the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.envs'
["local", "test", "prod"]
Let a list of variables provided by the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.vars'
["test_value"]
Let a list of prompts required by the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.prompts'
["prompt_value"]
Let a list of secrets required by the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.secrets'
["super_secret_value"]
Get the span of the config, if defined, in the request file. Otherwise it's null
.
reqlang parse ./examples/valid/post.reqlang | jq '.full.config[1]'
{
"start": 0,
"end": 204
}
Get the span of the request in the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.full.request[1]'
{
"start": 208,
"end": 388
}
Get the span of the response, if defined, in the request file. Otherwise it's null
.
reqlang parse ./examples/valid/post.reqlang | jq '.full.response[1]'
null
Get the span of all the template references (variables, prompts, secrets, providers), if defined, in the request file.
reqlang parse ./examples/valid/post.reqlang | jq '.full.refs'
[
[
{
"Provider": "env"
},
{
"start": 208,
"end": 388
}
],
[
{
"Variable": "test_value"
},
{
"start": 208,
"end": 388
}
],
[
{
"Prompt": "prompt_value"
},
{
"start": 208,
"end": 388
}
],
[
{
"Secret": "super_secret_value"
},
{
"start": 208,
"end": 388
}
]
]
If the request file is invalid, a list of errors will be returned instead.
reqlang parse examples/invalid/empty.reqlang
[
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 0
}
},
"severity": 1,
"message": "ParseError: Request file is an empty file"
}
]
Parse and template the request file then export it in different formats.
Usage: reqlang export [OPTIONS] <path>
Arguments:
<path> Path to request file
Options:
-e, --env <env> Resolve with an environment
-P, --prompt <prompts> Pass prompt values to resolve with
-S, --secret <secrets> Pass secret values to resolve with
-f, --format <format> Format to export [default: json] [possible values: http, curl, json]
-h, --help Print help
reqlang export examples/valid/status_code.reqlang --prompt status_code=200 --format json
# This is the same thing
reqlang export examples/valid/status_code.reqlang --prompt status_code=200
{
"verb": "GET",
"target": "https://httpbin.org/status/200",
"http_version": "1.1",
"headers": [],
"body": ""
}
reqlang export examples/valid/status_code.reqlang --prompt status_code=201 --format http
GET https://httpbin.org/status/201 HTTP/1.1
reqlang export examples/valid/status_code.reqlang --prompt status_code=400 --format curl
curl https://httpbin.org/status/400 --http1.1 -v
If the request file is invalid or there were errors templating, a list of errors will be returned instead.
reqlang export examples/invalid/empty.reqlang
[
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 0
}
},
"severity": 1,
"message": "ParseError: Request file is an empty file"
}
]
The reqlang
CLI can be run from a docker image.
docker build -t reqlang:0.1.0 .
A directory of request files can be mounted inside the container's /usr/local/src
directory to make them accessible.
docker run --rm --read-only \
-v "/$PWD/examples":/usr/local/src/examples:ro \
reqlang:0.1.0 \
export \
./examples/valid/delay.reqlang \
-f curl \
-P seconds=5 | bash
# HTTP/1.1 201 CREATED
# Date: Sat, 14 Dec 2024 19:20:26 GMT
# Content-Type: text/html; charset=utf-8
# Content-Length: 0
# Connection: keep-alive
# Server: gunicorn/19.9.0
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Credentials: true
The VS Code extension acts as an in-editor REST client.
The desktop client is a very simple GUI written in egui. It's mostly used for testing.
Please see CONTRIBUTING.md for details on how to contribute.