Bruno API Client Tutorial 2026: Setup, Collections & Bru-Language

Aktualisiert: 18. Mai 2026

Bruno hat 2025 etwas geschafft, was viele für unmöglich hielten: Postman die Open-Source-Krone abzunehmen. Über 30.000 GitHub-Stars in zwei Jahren, eine wachsende DACH-Community und ein Lizenzmodell, das CI/CD-Pipelines ohne Cloud-Account erlaubt. Bruno ist kein "Postman-Klon", sondern eine bewusste Antwort auf alles, was an Postman in den letzten Jahren genervt hat.

Der entscheidende Unterschied: Bruno speichert Collections als .bru-Dateien direkt auf der Festplatte. Kein Workspace-Lock-in, keine Cloud-Synchronisation, kein Account-Zwang. Du committest deine API-Tests genauso ins Git-Repo wie deinen Source-Code. Für Teams, die DSGVO-strikt oder offline arbeiten, ist das ein Game-Changer.

In diesem Tutorial baue ich mit dir die erste Bruno-Collection von Grund auf, zeige die Bru-Language im Detail, baue Tests mit Assertions ein und integriere das Ganze in eine GitHub-Actions-Pipeline. Du brauchst kein Postman-Vorwissen.

Inhaltsverzeichnis

Was ist Bruno und für wen lohnt es sich?

Bruno ist ein Open-Source-API-Client, gestartet 2023 vom indischen Entwickler Anoop M D, lizenziert unter MIT. Die Hauptzielgruppe sind Backend-Entwickler:innen und Test Automation Engineers, die API-Tests versionsverfolgt im Repository halten wollen.

Drei Eigenschaften, die Bruno einzigartig machen:

  • Filesystem-First: Collections sind Ordner mit .bru-Dateien auf der Disk, nicht in einer proprietären Cloud
  • Bru-Language: Eine textbasierte DSL für Requests, lesbar und Git-diff-freundlich
  • Offline-fähig: Du brauchst kein Account und kein Login, alles läuft lokal

Wer aus Postman umsteigt, gewinnt vor allem zwei Dinge: Code-Review für API-Tests (Bru-Files im Pull Request) und Compliance-Fähigkeit (keine Daten verlassen den Rechner ohne explizites Zutun). Für die volle Tool-Übersicht siehe unseren API-Testing-Tools 2026 Vergleich mit 8 weiteren Alternativen.

Installation und erstes Setup

Bruno gibt es als Desktop-App für Windows, macOS und Linux. Drei Wege zur Installation:

SystemMethodeBefehl
macOSHomebrewbrew install bruno
Windowswingetwinget install Bruno.Bruno
LinuxSnap / .debsudo snap install bruno
Cross-PlatformDownloadusebruno.com/downloads

Nach dem Start: Keine Anmeldung, kein Onboarding-Tutorial mit fünf Werbescreens. Du landest direkt im Workspace. Über File → Open Collection bindest du einen Ordner als Collection ein. Über File → New Collection legst du einen frischen Ordner an.

Erste Collection in 5 Minuten

Lege einen neuen Ordner an, z.B. my-api-tests/. In Bruno: File → New Collection, wähle den Ordner aus, gib ihr einen Namen ("My API Tests"). Bruno legt eine bruno.json als Manifest an.

Rechtsklick auf die Collection → New Request. Name "Get Users", Method GET, URL https://jsonplaceholder.typicode.com/users. Speichern. Bruno erzeugt eine Datei get-users.bru:

meta {
  name: Get Users
  type: http
  seq: 1
}

get {
  url: https://jsonplaceholder.typicode.com/users
  body: none
  auth: none
}

Klick auf Send. Du bekommst die Response inklusive Status, Headers, Timing. Das wars: Erste Collection, erster Request, erste Antwort. Keine Cloud, kein Login, alles auf deiner Disk.

Die Bru-Language: Syntax und Struktur

Die Bru-Language ist eine textbasierte DSL, optimiert auf Lesbarkeit und Git-Diffability. Jede .bru-Datei hat Blöcke in geschweiften Klammern. Die wichtigsten:

meta {
  name: Create User
  type: http
  seq: 2
}

post {
  url: {{baseUrl}}/users
  body: json
  auth: bearer
}

headers {
  Content-Type: application/json
  X-Request-ID: {{$randomUUID}}
}

auth:bearer {
  token: {{apiToken}}
}

body:json {
  {
    "name": "{{userName}}",
    "email": "{{userEmail}}"
  }
}

tests {
  test("Status ist 201", function() {
    expect(res.getStatus()).to.equal(201);
  });
  test("Response enthält ID", function() {
    expect(res.body.id).to.be.a('number');
  });
}

Drei Sachen, die du dabei mitnimmst: Variablen mit {{name}}, Builtin-Variablen wie {{$randomUUID}}, Test-Block mit Chai-Assertions. Das Format ist Git-diff-freundlich: Änderungen sind als textuelles Diff lesbar, anders als bei Postmans JSON-Export.

Variablen, Environments und Secrets

Bruno kennt vier Variablen-Ebenen, in absteigender Priorität:

  • Request-Variables: Im Request-Body direkt definiert
  • Folder-Variables: Per Folder-Settings
  • Collection-Variables: Auf Collection-Ebene
  • Environment-Variables: Pro Umgebung (dev, staging, prod)

Environments liegen als environments/dev.bru oder environments/prod.bru in der Collection. Beispiel:

vars {
  baseUrl: https://api.dev.example.com
  userName: dev-tester
}

vars:secret [
  apiToken
]

Secrets stehen nur als Platzhalter in der Bru-Datei. Der echte Wert kommt aus einer .env-Datei (gitignored) oder einer Bruno-internen Secret-Stelle. Du committest die Bru-Files, aber niemals die echten Tokens.

Authentication: Basic, Bearer, OAuth 2

Bruno unterstützt alle gängigen Auth-Mechanismen direkt im auth-Block:

MethodeBru-BlockUse-Case
Noneauth: noneÖffentliche APIs
Basicauth: basicLegacy-APIs mit User/Passwort
Bearerauth: bearerJWT, API-Keys via Header
API-Keyauth: apikeyIm Header oder Query-Param
OAuth 2auth: oauth2Authorization Code, Client Credentials
AWS Signatureauth: awsv4AWS-Service-APIs

Der OAuth-2-Flow funktioniert genauso, wie du es von Postman kennst: Authorization URL, Token URL, Client ID/Secret, Scopes. Bruno cached den Token in einer separaten Datei pro Environment und refresht ihn automatisch, wenn er abläuft.

Tests und Assertions in JavaScript

Tests in Bruno laufen im tests-Block. Die Assertion-Library ist chai, die Logik vanilla JavaScript. Du hast Zugriff auf req (Request), res (Response) und bru (Globale Helpers):

tests {
  test("Status 200", () => {
    expect(res.getStatus()).to.equal(200);
  });

  test("Response-Time < 500ms", () => {
    expect(res.getResponseTime()).to.be.below(500);
  });

  test("User-Liste hat 10 Einträge", () => {
    expect(res.getBody()).to.have.lengthOf(10);
  });

  test("Erster User hat E-Mail", () => {
    expect(res.getBody()[0].email).to.match(/@/);
  });
}

// Variablen aus Response setzen (für Folge-Requests)
script:post-response {
  bru.setVar("createdUserId", res.body.id);
}

Mit script:pre-request und script:post-response baust du komplexe Test-Ketten: Login-Request setzt Token-Variable, nachfolgende Requests nutzen ihn. Tests sind kein Add-on. Tests sind der Vertrag, den Code und CI miteinander schließen.

Git-Workflow: Collections versionieren

Hier glänzt Bruno wie kein anderer API-Client. Deine Collection ist ein ganz normaler Ordner mit Text-Dateien. Du committest sie ins gleiche Repo wie deinen Source-Code:

my-project/
├── src/
├── tests/
├── api-tests/              # Bruno Collection
│   ├── bruno.json
│   ├── environments/
│   │   ├── dev.bru
│   │   └── prod.bru
│   ├── users/
│   │   ├── get-users.bru
│   │   └── create-user.bru
│   └── auth/
│       └── login.bru
├── .gitignore              # enthält: .env, bruno-collection-secrets/
└── package.json

Pull Requests zeigen API-Test-Änderungen genauso wie Source-Code-Änderungen. Code-Review für deine Bru-Files. Diff-View zeigt: Welcher Endpoint wurde angepasst, welche Assertion verschärft, welcher Header geändert.

Das Pattern bewährt sich besonders, wenn API und Tests im selben Team gepflegt werden. Bei verteilten Teams nutzt du eine eigene api-tests-repo-Repository und referenzierst es als Git-Submodul oder per Subtree.

CLI-Runner und CI/CD-Integration

Bruno hat einen CLI-Runner namens @usebruno/cli, mit dem du Collections headless in Pipelines ausführst:

npm install -g @usebruno/cli

# Collection ausführen
bru run --env dev

# Nur bestimmten Folder
bru run users/ --env dev

# Mit JUnit-Report für CI
bru run --env prod --reporter-junit results.xml

# Mit HTML-Report
bru run --env prod --reporter-html report.html

Beispiel-Workflow für GitHub Actions (siehe auch GitHub Actions Tutorial):

- name: Bruno installieren
  run: npm install -g @usebruno/cli

- name: API Tests ausführen
  run: bru run api-tests/ --env staging --reporter-junit junit.xml
  env:
    API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}

- name: Test-Report hochladen
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: bruno-results
    path: junit.xml

Bruno CLI gibt einen Exit-Code zurück (0 für grün, 1 für rot), den die Pipeline auswertet. JUnit-XML lässt sich von jedem modernen Test-Reporter visualisieren.

Stolperfallen

Vier Fallen aus Kundenprojekten:

  • Secrets im Collection-Repo: Wenn du token: abc123 direkt in die Bru-Datei schreibst, ist er im Git versioniert. Lösung: Secrets-Block + .env (gitignored)
  • Postman-Import-Lücken: Der Importer für Postman-Collections ist gut, aber nicht 100% verlustfrei. Spezifische Postman-Scripts müssen oft manuell nachgezogen werden
  • Workspace-Trennung vergessen: Mehrere Collections in einem Workspace teilen sich keine Variablen automatisch. Environment muss pro Collection separat angelegt werden
  • Tests rot, aber Pipeline grün: Wenn du bru run ohne --fail-on-error aufrufst und die Reporter-Optionen vergisst, kommt der Exit-Code 0 trotz roten Tests. Immer mit JUnit oder HTML reporten

Fazit

Bruno ist 2026 der ernstzunehmende Postman-Herausforderer für alle, die Git-First arbeiten oder Cloud-Lock-in vermeiden wollen. Die Bru-Language ist schnell gelernt, die Test-Syntax ist vanilla JavaScript und der CLI-Runner integriert sich nahtlos in CI/CD-Pipelines. Für Solo-Devs ist der Free-Tier mehr als genug, für Teams gibt es seit Anfang 2026 einen Cloud-Sync-Tier mit Kollaboration.

Brauchst du Unterstützung beim Aufbau einer API-Test-Strategie, Migration von Postman oder CI/CD-Integration? Unser API-Testing-Service deckt Tool-Wahl, Pilotprojekte und Schulungen ab. Detaillierter Tool-Vergleich im API-Testing-Tools 2026 Artikel und direkter Schritt-für-Schritt-Vergleich mit Postman im Bruno vs Postman Artikel.

FAQ: Häufige Fragen zu Bruno

Ist Bruno wirklich kostenlos?

Ja. Die Desktop-App ist MIT-lizenziert und vollständig kostenlos für persönlichen und kommerziellen Einsatz. Bruno Cloud (optional, seit 2026) für Team-Sync kostet 4 USD/User/Monat, ist aber für die meisten Anwendungsfälle nicht zwingend.

Wie unterscheidet sich Bruno von Postman?

Hauptunterschiede: Bruno speichert lokal als Bru-Dateien (Postman in der Cloud), ist Open Source (Postman proprietär), hat keine Account-Pflicht und integriert sich natürlich in Git. Postman bietet dafür ausgereiftere Kollaborations-Features und ein größeres Ökosystem. Direkter Vergleich im Bruno vs Postman Artikel.

Kann ich meine Postman-Collection in Bruno importieren?

Ja. Bruno hat einen Postman-Importer (File → Import → Postman Collection). Der deckt 90 Prozent der Standard-Requests ab. Komplexe Pre-Request-Scripts und Test-Scripts müssen oft manuell angepasst werden, weil die Bruno-Syntax leicht abweicht.

Welche Authentifizierung unterstützt Bruno?

Bruno unterstützt Basic Auth, Bearer Token, API Key, OAuth 2 (alle Flows: Authorization Code, Client Credentials, Password, Implicit) und AWS Signature v4. Custom-Auth-Schemas baust du als Pre-Request-Script.

Funktioniert Bruno in CI/CD-Pipelines?

Ja, über den CLI-Runner @usebruno/cli. Er führt Collections headless aus, gibt einen Exit-Code zurück und kann JUnit- oder HTML-Reports erzeugen. Integration mit GitHub Actions, GitLab CI, Jenkins ist Standard.

Ist Bruno DSGVO-konform?

Die Desktop-App schickt keine Daten an externe Server (außer du nutzt Bruno Cloud freiwillig). Damit ist Bruno für DSGVO-strikte Setups deutlich einfacher zu bewerten als Cloud-First-Tools wie Postman. Für regulierte Branchen ist Bruno ein gangbarer Default.

Testautomatisierung Beratung

Sie möchten Ihre Testautomatisierung optimieren? Unsere Experten helfen Ihnen bei der Auswahl der richtigen Tools, Best Practices und CI/CD-Integration.

Jetzt anfragen

Finden Sie weitere interessante Artikel zum Thema: