Overview
The Bru language is Bruno’s domain-specific language for defining HTTP requests, collections, and environments. Files use the .bru extension and follow a block-based syntax that is both human-readable and machine-parsable.
File Types
There are three types of .bru files:
Request files - Define HTTP/GraphQL/gRPC requests
Collection files - Configure collection-level settings (collection.bru)
Environment files - Define environment variables (env.bru or named environments)
Basic Syntax
Block Structure
Bru files consist of blocks that define different aspects of a request or collection:
meta {
name: My Request
type: http
seq: 1
}
get {
url: https://api.example.com/users
body: none
auth: none
}
Block Types
There are three types of blocks:
Key-value pairs enclosed in curly braces: headers {
content-type: application/json
authorization: Bearer {{token}}
}
Free-form text content for bodies and scripts:
Array of items in square brackets: meta {
tags: [
regression
smoke-test
]
}
Dictionary Syntax
Basic Key-Value Pairs
headers {
content-type: application/json
accept: application/json
}
Quoted Keys
Use quotes for keys with special characters:
headers {
"key with spaces": value
"colon:header": value
"{braces}": value
"nested escaped \"quote\"": value
}
Disabled Items
Prefix with ~ to disable a key-value pair:
headers {
content-type: application/json
~authorization: Bearer {{token}}
~"disabled:header": value
}
Multiline Values
Use triple quotes for multiline values:
headers {
x-long-header: '''
This is a
multiline value
'''
}
Content Type Annotations
For multipart forms and file uploads:
body:multipart-form {
textField: '''
Line 1
Line 2
''' @contentType(text/plain)
}
HTTP Methods
Supported HTTP method blocks:
get {
url: https://api.example.com/users
body: none
auth: none
}
post {
url: https://api.example.com/users
body: json
auth: bearer
}
put {
url: https://api.example.com/users/123
body: json
auth: bearer
}
delete {
url: https://api.example.com/users/123
body: none
auth: bearer
}
patch {
url: https://api.example.com/users/123
body: json
auth: bearer
}
Other supported methods: options, head, connect, trace
Custom HTTP Methods
http {
method: CUSTOM
url: https://api.example.com/custom
body: none
auth: none
}
meta {
name: Request Name
type: http
seq: 1
tags: [
regression
smoke-test
]
}
Display name of the request
Request type: http, graphql, grpc, or ws
Sequence number for ordering requests
List of tags for organizing requests
Query Parameters
params:query {
page: 1
limit: 10
~filter: inactive
}
Path Parameters
params:path {
id: 123
userId: abc456
}
headers {
content-type: application/json
authorization: Bearer {{token}}
x-api-key: {{apiKey}}
~x-debug: true
}
Request Bodies
JSON Body
body:json {
{
"username": "john",
"email": "[email protected] ",
"profile": {
"age": 30
}
}
}
Text Body
body:text {
Plain text content
can span multiple lines
}
XML Body
GraphQL Query
body:graphql {
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
}
GraphQL Variables
body:graphql:vars {
{
"id": "123"
}
}
SPARQL Query
body:sparql {
SELECT * WHERE {
?subject ?predicate ?object .
}
LIMIT 10
}
body:form-urlencoded {
username: john
password: secret
~remember: true
}
body:multipart-form {
username: john
avatar: @file(/path/to/image.png)
description: '''
User profile
description
''' @contentType(text/plain)
}
File Upload
body:file {
file: @file(/path/to/file.pdf) @contentType(application/pdf)
~file: @file(/path/to/disabled.txt)
}
Authentication
No Authentication
get {
url: https://api.example.com
auth: none
}
Basic Auth
auth:basic {
username: admin
password: secret123
}
Bearer Token
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
}
API Key
auth:apikey {
key: x-api-key
value: my-secret-key
placement: header
}
Where to place the API key: header or query
Digest Auth
auth:digest {
username: john
password: secret
}
AWS Signature v4
auth:awsv4 {
accessKeyId: AKIAIOSFODNN7EXAMPLE
secretAccessKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
sessionToken: optional-session-token
service: execute-api
region: us-east-1
profileName: default
}
OAuth 2.0
Authorization Code
auth:oauth2 {
grant_type: authorization_code
callback_url: http://localhost:8080/callback
authorization_url: https://auth.example.com/authorize
access_token_url: https://auth.example.com/token
client_id: my-client-id
client_secret: my-client-secret
scope: read write
state: random-state-string
pkce: true
auto_fetch_token: true
auto_refresh_token: true
}
Client Credentials
auth:oauth2 {
grant_type: client_credentials
access_token_url: https://auth.example.com/token
client_id: my-client-id
client_secret: my-client-secret
scope: read write
}
Password Grant
auth:oauth2 {
grant_type: password
access_token_url: https://auth.example.com/token
username: [email protected]
password: secret
client_id: my-client-id
client_secret: my-client-secret
}
Variables
Pre-Request Variables
vars:pre-request {
timestamp: {{$timestamp}}
requestId: {{$uuid}}
@localVar: sensitive-data
}
Variables prefixed with @ are local and not saved to the file.
Post-Response Variables
vars:post-response {
token: $res.body.token
userId: $res.body.user.id
@sessionId: $res.body.sessionId
}
Assertions
assert {
$res.status: 200
$res.body.message: success
$res.body.user.id: 123
~$res.body.debug: enabled
}
Scripts
Pre-Request Script
script:pre-request {
const timestamp = Date.now();
bru.setVar("timestamp", timestamp);
const signature = crypto
.createHash('sha256')
.update(timestamp.toString())
.digest('hex');
bru.setVar("signature", signature);
}
Post-Response Script
script:post-response {
const body = res.getBody();
bru.setEnvVar("authToken", body.token);
}
Tests
tests {
test("Status should be 200", function() {
expect(res.getStatus()).to.equal(200);
});
test("Response should have token", function() {
const body = res.getBody();
expect(body.token).to.be.ok;
});
}
Documentation
docs {
This endpoint creates a new user account.
Requirements:
- Valid authentication token
- Unique email address
- Password must be at least 8 characters
}
Settings
settings {
timeout: 30000
followRedirects: true
maxRedirects: 5
encodeUrl: true
keepAliveInterval: 30
}
Request timeout in milliseconds (0 = no timeout)
Whether to follow HTTP redirects
Maximum number of redirects to follow
Whether to encode URL parameters
Keep-alive interval in seconds for WebSocket connections
Variable Interpolation
Use double curly braces to reference variables:
get {
url: {{baseUrl}}/users/{{userId}}
}
headers {
authorization: Bearer {{token}}
x-request-id: {{$uuid}}
}
body:json {
{
"timestamp": "{{$timestamp}}",
"user": "{{username}}"
}
}
Built-in Variables
{{$uuid}} - Generate a random UUID
{{$timestamp}} - Current Unix timestamp
{{$isoTimestamp}} - Current ISO 8601 timestamp
{{$randomInt}} - Random integer
{{$randomEmail}} - Random email address
Bru does not support inline comments. Use the docs block for documentation.
Complete Example
meta {
name: Create User
type: http
seq: 1
tags: [
users
create
]
}
post {
url: {{baseUrl}}/api/users
body: json
auth: bearer
}
params:query {
notify: true
~debug: false
}
headers {
content-type: application/json
x-request-id: {{$uuid}}
}
auth:bearer {
token: {{authToken}}
}
body:json {
{
"username": "johndoe",
"email": "[email protected] ",
"profile": {
"firstName": "John",
"lastName": "Doe"
}
}
}
vars:pre-request {
timestamp: {{$timestamp}}
}
vars:post-response {
userId: $res.body.id
userToken: $res.body.token
}
assert {
$res.status: 201
$res.body.id: isDefined
}
script:pre-request {
console.log("Creating user...");
}
tests {
test("User created successfully", function() {
expect(res.getStatus()).to.equal(201);
expect(res.getBody().id).to.be.ok;
});
}
docs {
Creates a new user account with the provided information.
Returns the created user object with ID and token.
}
settings {
timeout: 30000
followRedirects: true
}
See Also
Request Format Detailed request file format specification
Collection Format Collection and environment file formats