📚 Core Concepts — .bru Files, Collections & Scripting
Core Concepts — .bru Files, Collections: A .bru file is like a single recipe card: it lists the ingredients (URL, headers, body) and the steps (any scripts), all in one short, hu
A .bru file is like a single recipe card: it lists the ingredients (URL, headers, body) and the steps (any scripts), all in one short, human-readable block of text. A whole collection is just a recipe box — a folder full of these cards, organized into sub-folders the same way you'd group recipes by "breakfast" or "dinner".
The .bru File Format
collections/users/get.bru — what a real request file looks like:
Project Folder Structure
A typical Bruno collection on disk
Every single file here is plain text. Open any .bru file in VS Code and you can read and even edit it without ever opening the Bruno app.
Reasoning #1 — Why does Bruno split "Assert" and "Script" into two separate tabs instead of one script box like Postman's pm.test()? Most checks are trivially simple ("is status 200?", "does body.id equal 1?") — forcing every tester to write JavaScript for that is unnecessary friction, similar to how JUnit's @Test annotation handles the common case while you only drop into custom Java code for genuinely complex assertions. The Assert tab is the low-friction path; the Script tab is there exactly when logic gets too dynamic for a simple key-operator-value row.
Environments & Variables
LEGO analogy: an Environment file is like a labeled tray that holds the specific-colored bricks for one build — a "dev" tray with dull gray bricks (a safe sandbox server) and a "prod" tray with shiny gold bricks (the real server). You build the exact same model (the same request) but swap which tray you pull from — you never have to rebuild the request itself just to point it somewhere else.
Reasoning #2 — why variables instead of just hardcoding the URL? If a junior tester hardcodes https://api-dev.example.com into 40 different requests, switching to staging means editing 40 files and almost certainly missing one. With {{baseUrl}}, switching environments is a single dropdown change — every request updates automatically. This is the exact same reasoning Java engineers already know from externalizing config out of hardcoded strings into application.properties.
🎬 The Anatomy of a .bru File: How {{baseUrl}} Gets Resolved
script:post-response
No Environment Selected
The get.bru file is really four small sections: meta, get, headers, assert. Today's focus: how does {{baseUrl}} inside the get section become a real URL?