Bruno
Bruno API client for git-friendly, offline-first API testing with Bru markup language
You are an expert in Bruno for building and testing APIs with a git-native, offline-first workflow.
## Key Points
- name: Install Bruno CLI
- name: Run API tests
- Commit the entire Bruno collection directory to git alongside your API source code.
- Use `vars:secret` in environment files for tokens and passwords — they are not saved to disk.
- Use `seq` in meta blocks to control execution order when running a folder.
- Use `script:post-response` to chain requests by storing values in collection or environment variables.
- Keep one `.bru` file per request and organize by resource folder to match your API structure.
- Review `.bru` file diffs in pull requests — they are human-readable plain text.
- Forgetting to set `seq` ordering, causing requests to run in arbitrary order when folder-level execution depends on chaining.
- Using `bru.setVar` (collection variable) when `bru.setEnvVar` (environment variable) is needed, or vice versa — they have different scopes and lifetimes.
- Not selecting the correct environment in the GUI or CLI before running, leading to empty `{{variables}}`.
- Editing `.bru` files by hand with incorrect syntax — the Bru format is whitespace-sensitive in body blocks.
## Quick Example
```bash
# macOS
brew install bruno
# npm (CLI runner)
npm install -g @usebruno/cli
```
```json
{
"version": "1",
"name": "My API",
"type": "collection"
}
```skilldb get api-testing-skills/BrunoFull skill: 231 linesBruno — API Testing
You are an expert in Bruno for building and testing APIs with a git-native, offline-first workflow.
Core Philosophy
Overview
Bruno is an open-source API client that stores collections as plain files on your filesystem using its own Bru markup language. Unlike cloud-synced tools, Bruno collections live in your repository, are version-controlled with git, and work fully offline. It supports scripting, environment variables, assertions, and a CLI runner.
Setup & Configuration
Installation
# macOS
brew install bruno
# npm (CLI runner)
npm install -g @usebruno/cli
The desktop app is available from usebruno.com for Windows, macOS, and Linux.
Collection structure
A Bruno collection is a directory of .bru files:
api-collection/
bruno.json # Collection metadata
environments/
dev.bru
staging.bru
auth/
login.bru
refresh-token.bru
users/
list-users.bru
create-user.bru
get-user.bru
bruno.json
{
"version": "1",
"name": "My API",
"type": "collection"
}
Environment files
# environments/dev.bru
vars {
base_url: http://localhost:3000/api
auth_email: test@test.com
auth_password: secret123
}
vars:secret [
auth_token
]
Core Patterns
Basic request (.bru file)
meta {
name: List Users
type: http
seq: 1
}
get {
url: {{base_url}}/users
body: none
auth: bearer {{auth_token}}
}
headers {
Accept: application/json
}
query {
page: 1
per_page: 25
}
assert {
res.status: eq 200
res.body.length: gt 0
}
tests {
test("response contains users array", function() {
const data = res.getBody();
expect(data).to.be.an('array');
expect(data[0]).to.have.property('id');
});
}
POST request with JSON body
meta {
name: Create User
type: http
seq: 2
}
post {
url: {{base_url}}/users
body: json
auth: bearer {{auth_token}}
}
body:json {
{
"name": "Alice",
"email": "alice@example.com",
"role": "member"
}
}
assert {
res.status: eq 201
res.body.name: eq Alice
res.body.id: isDefined
}
script:post-response {
bru.setVar("created_user_id", res.body.id);
}
Pre-request scripting for auth
meta {
name: Login
type: http
seq: 1
}
post {
url: {{base_url}}/auth/login
body: json
auth: none
}
body:json {
{
"email": "{{auth_email}}",
"password": "{{auth_password}}"
}
}
script:post-response {
const body = res.getBody();
bru.setEnvVar("auth_token", body.token);
}
Running with the CLI
# Run entire collection
bru run --env dev
# Run a specific folder
bru run auth/ --env dev
# Run a single request
bru run users/create-user.bru --env dev
# Output as JUnit for CI
bru run --env dev --reporter junit --output results.xml
CI/CD integration (GitHub Actions)
- name: Install Bruno CLI
run: npm install -g @usebruno/cli
- name: Run API tests
run: bru run api-collection/ --env ci --reporter junit --output results.xml
env:
BASE_URL: ${{ secrets.API_BASE_URL }}
Best Practices
- Commit the entire Bruno collection directory to git alongside your API source code.
- Use
vars:secretin environment files for tokens and passwords — they are not saved to disk. - Use
seqin meta blocks to control execution order when running a folder. - Use
script:post-responseto chain requests by storing values in collection or environment variables. - Keep one
.brufile per request and organize by resource folder to match your API structure. - Review
.brufile diffs in pull requests — they are human-readable plain text.
Common Pitfalls
- Forgetting to set
seqordering, causing requests to run in arbitrary order when folder-level execution depends on chaining. - Using
bru.setVar(collection variable) whenbru.setEnvVar(environment variable) is needed, or vice versa — they have different scopes and lifetimes. - Not selecting the correct environment in the GUI or CLI before running, leading to empty
{{variables}}. - Editing
.brufiles by hand with incorrect syntax — the Bru format is whitespace-sensitive in body blocks. - Storing secrets directly in environment
.brufiles that get committed to version control.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add api-testing-skills
Related Skills
API Mocking
API mocking with MSW (Mock Service Worker) and Prism for development and testing
Contract Testing
Pact contract testing for consumer-driven API contracts between microservices
Httpie
HTTPie CLI for human-friendly API testing, scripting, and debugging from the terminal
Load Testing
k6 load testing for API performance, stress testing, and threshold-based CI checks
Postman
Postman collections, environments, pre-request scripts, tests, and Newman CLI automation
Supertest
Supertest for Node.js HTTP assertion testing with Express, Koa, and Fastify