Aller au contenu principal

Bruno - Un outil à la Postman - GUI et CLI

Découvrez Bruno, un outil de test d'API puissant à la Postman, avec support GUI et CLI.

· 7 minutes de lecture
Voir la source
Christophe
Amoureux de Markdown, WSL et Docker ~ Curieux insatiable.
Cet article existe aussi en anglais — lire la version originale.

Traduction automatique

Cette traduction a été produite par une IA. Elle peut contenir des contresens ou des maladresses, en particulier sur les termes techniques. En cas de doute, consultez l'article de référence en anglais — c'est lui qui fait foi.

Journal des modifications

Dernière
Bruno v3 released January 2026; official Docker image is now usebruno/cli (the AlertBox custom-image workaround may no longer be needed).

Bruno - A postman-like tool - GUI and CLI

⚡
TL;DR

Cet article présente Bruno, une alternative libre et gratuite à Postman pour tester vos API. Il vous guide dans la mise en place d'une API Python FastAPI d'exemple, puis dans son test via l'interface graphique et le CLI de Bruno. Vous apprendrez à créer des requêtes, gérer des environnements et lancer des tests depuis la ligne de commande avec Docker, y compris comment gérer les problèmes réseau classiques. L'article aborde aussi l'ajout d'assertions pour valider vos tests.

Bruno est un outil comme Postman, utilisable gratuitement. Tout est stocké sur votre machine : vous pouvez donc conserver les fichiers dans votre codebase et les envoyer dans votre outil de versioning, par exemple.

Comme ces fichiers vivent dans votre repository, la partie CLI de Bruno se transforme naturellement en job de CI — voir GitLab - Exécuter Docker-out-of-Docker dans votre CI. Et pour vérifier le contrat de l'API plutôt que ses réponses, il y a Validez votre schéma OpenAPI face aux standards REST Belgif.

Lancer Bruno​

Une fois installé, démarrez simplement bruno depuis la ligne de commande pour lancer l'interface :

Bruno homepage

Commençons par le commencement : créons une collection.

Creating a collection

Et, comme on est malins, créons aussi un environnement (pour définir l'URL racine de notre site une seule fois) :

Create the root environment variable

Nous voilà prêts, créons une nouvelle requête :

Create a request

Récupérer une blague au hasard :

Getting a random joke

Une fois la requête créée, appuyez sur CTRL+ENTER ou cliquez sur la flèche de droite :

Running a request

Pourquoi ça fonctionne​

  • Tout — collection, environnements, requêtes — est stocké sous forme de fichiers texte .bru dans votre repo : une requête se diffe et se relit comme du code.
  • La même collection s'exécute de deux façons : en mode exploration dans la GUI, ou en vérifications scriptées depuis le CLI — pas d'outil séparé à garder synchronisé.
  • Une collection correspond à un dossier unique et propre (voir la section Ouvrir le projet avec VSCode ci-dessous) ; rien ne vit dans une base de données ou un compte cloud que vous ne contrôlez pas.

Installation​

Pour installer la GUI de Bruno sur ma distribution Ubuntu, je lance ces commandes :

>user@machine: ~/yourproject

Consultez la documentation officielle Download & Install pour plus d'infos.

Optionnel - Démarrer une API de test pour essayer Bruno (cliquez pour les détails)

Bruno a besoin de quelque chose à interroger. Si vous n'avez pas d'API sous la main, voici une API de test FastAPI en une minute, empruntée à « Python - Fast API - Créez votre API JSON en Python en une minute » :

  • Lancez mkdir /tmp/fastapi && cd $_ pour créer un dossier temporaire et vous y placer
  • Créez un Dockerfile avec le contenu ci-dessous
  • Créez un main.py avec le contenu ci-dessous
  • Lancez la commande docker build -t python-fastapi . && docker run --detach -v .:/app -p 82:82 python-fastapi pour démarrer le serveur
  • Ouvrez un navigateur sur http://127.0.0.1:82/jokes pour voir une première blague (F5 pour en obtenir une nouvelle ; au hasard)
# We'll use the latest version of Python and the smaller image in size (i.e. `slim`)
FROM python:slim
# We'll define the default folder in the image to /app
WORKDIR /app
# The only dependency we need is fastapi
RUN pip install --no-cache-dir 'fastapi[standard]'
# We need to copy our Python script in the image
COPY main.py main.py
# Run Uvicorn with hot reload so any changes to our main.py script will force
# the server to invalidate the cache and refresh the page.
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "82"]

Ce Dockerfile crée une image Docker légère basée sur Python qui installe FastAPI, copie le script main.py dans l'image, puis démarre un serveur Uvicorn en mode rechargement automatique sur le port 82. C'est une configuration typique pour développer une API web rapidement.

from random import choice
from fastapi import FastAPI
app = FastAPI()
jokes = [
"Why do programmers always mix up Halloween and Christmas? Because Oct 31 == Dec 25",
"Why don't programmers like nature? Because it's full of bugs!",
"How many programmers does it take to change a light bulb? None. That's a hardware problem.",
"What do you call 8 hobbits? A hobbyte",
"What is this [“hip”, ”hip”]? hip hip array!"
]
@app.get("/jokes")
async def get_jokes():
"""
Returns a random joke from the list.
"""
return {"joke": choice(jokes)}
@app.get("/jokes/{joke_id}")
def read_item(joke_id: int):
"""
Returns a specific joke (between 0 and 4).
"""
try:
return {"joke": jokes[joke_id]}
except IndexError:
return {"error": f"Joke with ID {joke_id} not found."}, 404

Ce code crée une API web simple avec FastAPI qui expose deux endpoints pour récupérer des blagues : l'un retourne une blague aléatoire, l'autre retourne une blague spécifique par son identifiant. Les blagues sont stockées dans une liste et l'API gère les cas où l'ID demandé n'existe pas.

Ouvrir le projet avec VSCode​

En ouvrant le projet dans VSCode, on voit un nouveau dossier appelé Jokes (notre collection) avec très peu de fichiers : environments/dev.bru où se trouvent les variables de notre environnement, un fichier générique bruno.json, puis notre requête dans Get a random joke.bru.

Structure très propre, non ?

The project in VSCode

Exécuter des requêtes depuis la ligne de commande​

Bruno propose une image Docker : elle va nous aider à automatiser l'exécution de nos requêtes depuis la ligne de commande.

Attention

À la mi-juillet 2025, je n'ai pas réussi à faire fonctionner cette image comme prévu. Je tombais sur des erreurs Cannot read properties of undefined (reading 'headers') alors que, à mon avis, tout était correctement configuré.

J'ai donc cherché une autre image et j'ai trouvé celle-ci : davidkarlsen/bruno-image. Pas de chance non plus, même la dernière version de l'époque (2.7.0) posait problème.

En regardant le Dockerfile, j'ai vu que le fichier était vraiment simple et qu'une version plus récente de Bruno était sortie : la 2.8.

Bref, je vais créer ma propre image Docker et voir si ça va mieux.

Créer notre propre image Bruno CLI​

Créons un fichier bruno.Dockerfile avec le contenu suivant :

FROM node:lts-bookworm-slim
// highlight-next-line
RUN npm install -g @usebruno/cli@2.8.0
ENTRYPOINT ["bru"]
CMD ["run"]

Ce Dockerfile crée une image Docker basée sur Node.js LTS avec l'environnement Debian Bookworm allégé. Il installe l'outil CLI Bruno (un client API) globalement, puis configure le conteneur pour exécuter la commande bru par défaut.

On crée notre image comme ceci : docker build --file bruno.Dockerfile -t bruno-image . (on peut la vérifier en lançant docker run -it --rm bruno-image --version ; on devrait voir 2.8.0).

Comme on vient de le voir, notre collection est stockée dans le dossier Jokes et notre environnement dans environments/dev.bru. Mais un container ne peut pas joindre 127.0.0.1 sur votre host : créez donc un second fichier d'environnement, environments/dev-docker.bru, qui pointe vers host.docker.internal :

vars {
host: http://host.docker.internal:82
}

Lancez maintenant la collection avec cet environnement :

>user@machine: ~/yourproject

Bruno CLI is working

Sous le capot (à sauter si vous voulez juste l'utiliser)​

Pourquoi ne pas simplement réutiliser environments/dev.bru, celui que la GUI utilise déjà ? Parce que l'exécuter tel quel depuis le CLI échoue :

Running for the first time the collection from the CLI

Regardez le message d'erreur connect ECONNREFUSED 127.0.0.1:82 : le container Bruno CLI essaie de joindre le serveur web sur 127.0.0.1, mais cette adresse, c'est le container lui-même, pas votre host. environments/dev.bru ressemble à ceci :

vars {
root: http://127.0.0.1:82
}

Ça fonctionne depuis la GUI, parce que la GUI tourne directement sur votre host — là, 127.0.0.1 est votre machine. Un curl -v http://127.0.0.1:82/jokes depuis la ligne de commande de votre host fonctionne aussi :

>user@machine: ~/yourproject

Un container a son propre namespace réseau : 127.0.0.1 à l'intérieur n'atteint jamais l'host. C'est exactement ce que corrigent environments/dev-docker.bru et host.docker.internal ci-dessus. Effet de bord : ce nouveau fichier d'environnement ne fonctionnera pas depuis la GUI de Bruno — on peut sélectionner dev et tout marche, mais pas dev-docker :

Having a second configuration file

Ajouter quelques assertions​

L'intérêt de l'outil CLI, bien sûr, c'est de pouvoir lancer des assertions depuis la ligne de commande et s'assurer que l'API fonctionne toujours.

Mettons à jour le fichier Get a random joke.bru comme ceci :

meta {
name: Get a random joke
type: http
seq: 1
}
get {
url: {{host}}/jokes
body: none
auth: none
}
<!-- highlight-start -->
assert {
res.status: eq 200
res.headers["content-type"]: isDefined
res.headers["content-type"]: isString
res.headers["content-type"]: contains "application/json"
res.body.joke.length: gt 0
res.body.joke: isString
}
<!-- highlight-end -->
settings {
encodeUrl: true
}

Ce fichier définit une requête HTTP GET vers un endpoint de blagues et valide que la réponse est correcte — le statut doit être 200, l'en-tête content-type doit être défini et contenir 'application/json', et le corps doit contenir une propriété joke qui est une chaîne non vide.

Bruno CLI is running assertions

Conclusion​

Bruno vous offre un workflow à la Postman — collections, environnements, assertions — sans quitter votre repository : chaque requête est un fichier .bru que vous pouvez differ, relire et versionner comme le reste de votre code. La GUI sert à explorer et à construire une collection ; le CLI, emballé dans votre propre image Docker, transforme cette même collection en vérification reproductible.

Cette reproductibilité, c'est exactement ce qu'attend un pipeline de CI. Voir GitLab - Exécuter Docker-out-of-Docker dans votre CI pour lancer cette image CLI comme job, et Validez votre schéma OpenAPI face aux standards REST Belgif pour vérifier le contrat de l'API, pas seulement ses réponses.


🦋 Rejoignez la conversation sur BlueskyUn compte Bluesky gratuit est nécessaire pour aimer, repartager ou commenter. Ce bloc interroge l'API publique de Bluesky directement depuis votre navigateur (sans cookie, sans connexion) — votre adresse IP parvient donc aux serveurs de Bluesky dès que cette section se charge.

Chargement des commentaires…

Cet article vous a-t-il été utile ?
Avez-vous suivi les étapes de cet article ?