Skip to main content

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:
  1. Request files - Define HTTP/GraphQL/gRPC requests
  2. Collection files - Configure collection-level settings (collection.bru)
  3. 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:
body:json {
  {
    "username": "john",
    "email": "[email protected]"
  }
}
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
}

Request Metadata

meta {
  name: Request Name
  type: http
  seq: 1
  tags: [
    regression
    smoke-test
  ]
}
name
string
required
Display name of the request
type
string
required
Request type: http, graphql, grpc, or ws
seq
number
Sequence number for ordering requests
tags
array
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

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

body:xml {
  <user>
    <name>John</name>
    <email>[email protected]</email>
  </user>
}

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
}

Form URL Encoded

body:form-urlencoded {
  username: john
  password: secret
  ~remember: true
}

Multipart Form

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
}
placement
string
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
}
timeout
number
Request timeout in milliseconds (0 = no timeout)
followRedirects
boolean
Whether to follow HTTP redirects
maxRedirects
number
Maximum number of redirects to follow
encodeUrl
boolean
Whether to encode URL parameters
keepAliveInterval
number
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

Comments

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

Build docs developers (and LLMs) love