A file format specification for defining HTTP requests, response assertions, and configuration in "request files".
- Use HTTP request and response messages
- Easy to read, write, and diff
- Lives in source control
- Just a markdown file
- Templating with variables, prompts, and secret values
- Environments with environment specific variable values
- Client/implementation agnostic
- Statically typed expression language in the templates
- Chaining requests
- Response body mapping/transformation/extraction
- Authenticated requests (e.g. OAuth2) configuration
- Project workspaces
Request files are templated markdown files containing an HTTP request message, HTTP response message assertion (optional), and configuration (optional).
# HTTP Request Message
The [request](https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#http_requests) is defined inside of a markdown code fence block with `%request` as the language.
The request is the only required part of the request file.
```%request
GET https://example.com HTTP/1.1
```# HTTP Response Message Assertion
The expected HTTP response message assertion is what the actual response will be compared to. It's written inside of a `%response` markdown code block.
The assertion applies the following matching rules:
- Exact match of status code and text
- Exact match of individual headers
- Exact match of the body
```%response
HTTP/1.1 200 OK
```
The request, response, and configuration can be defined in any order.
```%request
GET https://example.com HTTP/1.1
```# Configuration
The configuration defines environments, variables, prompts, and secrets accessible during templating. It's written in a `%config` markdown code block.
```%config
# The configuration is written in TOML so comments like this are ignored during parsing.
```
```%request
GET https://example.com HTTP/1.1
```# Environments & Variables
Variables are declared first then defined in the environment declarations.
Here the variable `var1` is declared then defined in the environments `local` and `prod`.
```%config
[[vars]]
name = "var1"
[envs.local]
var1 = "local value"
[envs.prod]
var1 = "production value"
```
The variable `var1` is then templated using `{{:var1}}`.
```%request
GET https://example.com HTTP/1.1
x-value: {{:var1}}
x-env: {{@env}}
```
The environment used at request file execution time can also be templated using `{{@env}}`.# Default Variable Values
Variables can also have a default value defined.
```%config
[[vars]]
name = "var1"
default = "default value"
[envs.local]
# No value for `var1` is defined so it will be "default value" in the local environment.
[envs.prod]
var1 = "production value"
```
```%request
GET https://example.com HTTP/1.1
x-value: {{:var1}}
x-env: {{@env}}
```
The environment used at request file execution time can also be templated using `{{@env}}`.# Secrets
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 are optional but if they are declared, they must be at the top of the config block (due to how TOML parses tables).
```%config
secrets = ["api_key"]
```
```%request
GET https://example.com HTTP/1.1
x-api-key: {{!api_key}}
```# Prompts
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.
```%config
[[prompts]]
name = "tags"
```
```%request
GET https://example.com/posts?tags={{?tags}} HTTP/1.1
```See all examples for more request files.
The reqlang crate is a library working with request files.
use reqlang::prelude::*;
let request_file_text = fs::read_to_string("./path/to/requestfile.reqlang")
.expect("Should have been able to read the file");
const ast = Ast::from(&request_file_text);
const parsed_request_file = parse(&ast).expect("should be a valid request file");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).
reqlangCommand to work with request files
Usage: reqlang [COMMAND]
Commands:
export Export request to specified format
ast Produce an AST for a request file
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, body]
-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=200HTTP/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: *
Run the response assertion, if defined in the request file, the response will be compared to the expected response.
reqlang run examples/valid/mismatch_response.reqlang --testSee: mismatch_response.reqlang
HTTP/1.1 200 OK
connection: keep-alive
server: gunicorn/19.9.0
access-control-allow-origin: *
access-control-allow-credentials: true
date: Sun, 02 Feb 2025 03:55:33 GMT
content-type: application/json
content-length: 429
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}
Response assertion failed:
-HTTP/1.1 201 Created
+HTTP/1.1 200 OK
-x-test-value: ...
{
"slideshow": {
- "author": "Yours Truly",
+ "author": "Yours Truly",
+ "date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
- "title": "Test Slide Show"
- },
- "extra": true
+ "title": "Sample Slide Show"
+ }
}
-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]'nullGet 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"
}
]Produce an AST for a request file.
Usage: reqlang ast <path>
Arguments:
<path> Path to request file
Options:
-h, --help Print help
reqlang ast examples/valid/as_markdown.reqlang[
[
{
"Comment": "# Request Files Are Markdown Files\n\nAnything outside of the config, request, or response code blocks is treated as markdown. This lets you document your request files in a way that is easy to read and understand.\n\n## Config\n\nPrompt the user for the `status_code` to return.\n\n"
},
{
"start": 0,
"end": 275
}
],
[
{
"ConfigBlock": [
"[prompts]\n# Status code the response will return\nstatus_code = \"\"",
{
"start": 286,
"end": 352
}
]
},
{
"start": 275,
"end": 355
}
],
[
{
"Comment": "\n\n## Request\n\nThis will respond with the prompted `status_code`.\n\n"
},
{
"start": 355,
"end": 421
}
],
[
{
"RequestBlock": [
"GET https://httpbin.org/status/{{?status_code}} HTTP/1.1",
{
"start": 433,
"end": 490
}
]
},
{
"start": 421,
"end": 493
}
]
]reqlang ast examples/valid/as_markdown.reqlang | jq 'map(select(.[0] | has("Comment")))'[
[
{
"Comment": "# Request Files Are Markdown Files\n\nAnything outside of the config, request, or response code blocks is treated as markdown. This lets you document your request files in a way that is easy to read and understand.\n\n## Config\n\nPrompt the user for the `status_code` to return.\n\n"
},
{
"start": 0,
"end": 275
}
],
[
{
"Comment": "\n\n## Request\n\nThis will respond with the prompted `status_code`.\n\n"
},
{
"start": 355,
"end": 421
}
]
]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, body]
-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 httpGET https://httpbin.org/status/201 HTTP/1.1
reqlang export examples/valid/status_code.reqlang --prompt status_code=400 --format curlcurl https://httpbin.org/status/400 --http1.1 -vreqlang export examples/valid/base64decode.reqlang --format bodyHTTPBIN is awesome
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.
Please see CONTRIBUTING.md for details on how to contribute.
You can follow the development in this Bluesky thread.
