Tired of your API collections living in the cloud, locked behind a subscription, and impossible to version control? Bruno is a revolutionary open-source API client that stores everything as plain text files - right in your git repository.
Git-Native API Collections
Your API tests live alongside your code. Version control, branch, merge, and collaborate - just like your source code. No cloud sync, no vendor lock-in.
Bruno in Action
Bruno stores each request as a .bru file using a simple, readable format that plays perfectly with git:
Bruno collections are just files in your git repo - branch, merge, and review API changes like code
Key Features
File-Based Storage
Collections stored as plain text .bru files. Human-readable, diff-friendly, no proprietary formats
Git-Friendly
Version control your API tests. Branch, merge, PR review - works with any git workflow
No Cloud Required
Everything runs locally. No accounts, no sync, no subscriptions. Your data stays yours
Scripting Support
Pre/post request scripts in JavaScript. Set variables, validate responses, chain requests
Practical Examples
Here's what a Bruno .bru file looks like - simple, readable, and git-friendly:
meta {
name: Login User
type: http
seq: 1
}
post {
url: {{baseUrl}}/api/auth/login
body: json
auth: none
}
headers {
Content-Type: application/json
}
body:json {
{
"email": "{{email}}",
"password": "{{password}}"
}
}
script:post-response {
// Save token for subsequent requests
if (res.status === 200) {
bru.setEnvVar("authToken", res.body.token);
}
}
Environment files keep your secrets separate and environment-specific:
vars {
baseUrl: http://localhost:3000
email: dev@example.com
password: devpassword123
}
vars:secret [
authToken
]
Pre-request scripts let you set up dynamic values:
meta {
name: Create User
type: http
}
post {
url: {{baseUrl}}/api/users
body: json
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
body:json {
{
"name": "{{$randomFullName}}",
"email": "{{$timestamp}}@test.com"
}
}
script:pre-request {
// Generate unique test data
const timestamp = Date.now();
bru.setVar("uniqueId", timestamp);
}
assert {
res.status: eq 201
res.body.id: isDefined
}
Why Developers Love It
- ✓ True Version Control - API changes show up in git diffs and code reviews, not hidden in cloud sync
- ✓ No Subscription Needed - 100% free and open source. No paid tiers, no feature limits
- ✓ Team Collaboration via Git - Use your existing git workflow. No inviting teammates to yet another platform
- ✓ Postman Import - Migrate existing collections with one click. No rewriting from scratch
- ✓ Offline First - Works without internet. No cloud dependency means no cloud outages
-
✓
CLI for CI/CD - Run collections in your pipeline with
bru run
Bruno vs Postman Comparison
| Feature | Bruno | Postman |
|---|---|---|
| Storage | Local files in git | Cloud-synced |
| Version Control | Native git support | Workspace history (limited) |
| Pricing | Free forever | Free tier + paid plans |
| Offline Mode | Full functionality | Limited without sync |
| Data Privacy | 100% local | Cloud-stored |
| Team Collaboration | Git branches/PRs | Paid team workspaces |
| CI/CD Integration | CLI included free | Newman (separate tool) |
Cheatsheet
Quick reference for the .bru file format and common patterns:
| Block | Purpose | Example |
|---|---|---|
meta { } |
Request metadata | name: Get Users |
get/post/put { } |
HTTP method & URL | url: {{baseUrl}}/api |
headers { } |
Request headers | Authorization: Bearer {{token}} |
body:json { } |
JSON request body | { "key": "value" } |
auth:bearer { } |
Bearer token auth | token: {{authToken}} |
script:pre-request { } |
Run before request | bru.setVar("ts", Date.now()) |
script:post-response { } |
Run after response | bru.setEnvVar("id", res.body.id) |
assert { } |
Response assertions | res.status: eq 200 |
Pro Tips
Chain Requests with Variables
Use bru.setVar() in post-response scripts to pass data between requests. Perfect for auth flows where login returns a token needed for subsequent calls.
Run in CI/CD with bru CLI
Install the CLI with npm i -g @usebruno/cli and run collections in your pipeline: bru run --env dev. Get test results in JUnit format for CI integration.
Migrate from Postman
Export your Postman collection as JSON, then use Bruno's import feature. Your requests, environments, and even scripts will be converted to .bru files automatically.
Secure Secrets with vars:secret
Mark sensitive variables with vars:secret block. These won't be stored in files - they're entered at runtime or via environment variables in CI.
Organize with Folders
Create subfolders to group related requests (auth/, users/, products/). Each folder can have its own folder-level scripts and headers that apply to all requests inside.
Getting Started
-
1
Download Bruno
Visit usebruno.com and download for Windows, Mac, or Linux. Also available via brew, snap, and chocolatey.
-
2
Create or Import a Collection
Start fresh with "New Collection" or import existing Postman/Insomnia collections. Choose a folder inside your project repo.
-
3
Add to Git
Run
git add .to track your collection. Now your API tests are part of your codebase. -
4
Start Testing
Create requests, set up environments, and run your API tests. Changes auto-save to .bru files.
Get Bruno - Free Forever
Stop paying for cloud-synced API clients. Version control your API tests like code with Bruno.
Download Bruno