Comandos de Bruno
Bruno es un cliente API ligero, de código abierto y nativo de Git diseñado para pruebas y desarrollo de APIs. A diferencia de Postman, Bruno almacena tus colecciones como archivos de texto plano (formato .bru) que se pueden versionar con Git, lo que lo hace ideal para colaboración en equipo e integración con pipelines CI/CD.
Instalación
| Plataforma | Comando |
|---|---|
| macOS (Homebrew) | brew install bruno |
| Linux (Snap) | snap install bruno |
| Linux (APT) | sudo apt-get install bruno |
| Windows (Chocolatey) | choco install bruno |
| npm | npm install -g @usebruno/cli |
| Descargar | Visita usebruno.com/downloads |
Empezar
Lanzar la interfaz gráfica de Bruno
bruno
Crear una nueva colección
bruno create-collection my-api-collection
Abrir colección existente
bruno /path/to/collection
Gestión de colecciones
Estructura de colecciones
Bruno almacena colecciones como directorios con archivos .bru:
my-api-collection/
├── bruno.json # Metadatos de colección
├── environments/
│ ├── Development.json
│ └── Production.json
├── auth/
│ └── auth.bru
└── users/
├── get-all-users.bru
├── create-user.bru
└── update-user.bru
Importar desde Postman
# En la interfaz de Bruno: Import → Selecciona JSON de colección de Postman
# O usa CLI (si está disponible en tu versión de Bruno)
Organizar solicitudes en carpetas
Crea carpetas dentro de tu colección para organizar solicitudes:
- Haz clic derecho en el árbol de colecciones → Nueva carpeta
- Nombra carpetas lógicamente (ej: usuarios, productos, auth)
- Arrastra solicitudes entre carpetas
Exportar colección
# Las colecciones se almacenan como archivos simples (formato .bru)
# Simplemente haz commit a Git o comparte el directorio
Lenguaje Bru (formato de solicitud)
Bruno usa archivos .bru—un lenguaje de marcado simple y legible para solicitudes.
Archivo de solicitud básico
meta {
name: Get All Users
type: http
seq: 1
}
get {
url: {{baseUrl}}/api/users
auth: bearer
}
params:query {
limit: 10
offset: 0
}
headers {
Content-Type: application/json
User-Agent: Bruno/v1
}
auth:bearer {
token: {{authToken}}
}
Solicitud con cuerpo
meta {
name: Create User
type: http
seq: 2
}
post {
url: {{baseUrl}}/api/users
}
headers {
Content-Type: application/json
}
body:json {
{
"name": "John Doe",
"email": "john@example.com",
"role": "admin"
}
}
Solicitud de datos de formulario
meta {
name: Upload Profile Picture
type: http
seq: 3
}
post {
url: {{baseUrl}}/api/users/{{userId}}/avatar
}
body:form-urlencoded {
username: johndoe
email: john@example.com
}
Multipart de formulario (carga de archivo)
meta {
name: Upload File
type: http
seq: 4
}
post {
url: {{baseUrl}}/api/files/upload
}
body:multipartForm {
file: @/path/to/file.pdf
description: My document
}
Comandos CLI
Ejecutar colección o solicitud
# Ejecutar colección completa
bru run /path/to/collection
# Ejecutar solicitud específica
bru run /path/to/collection/requests/get-users.bru
# Ejecutar con entorno específico
bru run /path/to/collection --env Production
# Ejecutar con formato de reporte JSON
bru run /path/to/collection --reporter json
# Ejecutar con reporte HTML
bru run /path/to/collection --reporter html --output report.html
Reporteros disponibles
| Reportero | Comando |
|---|---|
| CLI (predeterminado) | bru run collection --reporter cli |
| JSON | bru run collection --reporter json |
| HTML | bru run collection --reporter html --output report.html |
| JUnit | bru run collection --reporter junit |
Ejecutar con variables
# Pasar variables de entorno
bru run /path/to/collection --env Development
# Sobrescribir variable específica
bru run /path/to/collection --env Production --variable apiKey=abc123
Fallar en error
# Salir con estado no cero si alguna prueba falla (útil para CI/CD)
bru run /path/to/collection --failOnError
Salida detallada
# Mostrar información detallada de solicitud/respuesta
bru run /path/to/collection --verbose
Variables de entorno
Crear archivo de entorno
Los archivos de entorno se almacenan como environments/EnvName.json:
{
"baseUrl": "https://api.example.com",
"apiKey": "your-api-key-here",
"authToken": "bearer-token",
"userId": "12345",
"timeout": 5000
}
Usar variables en solicitudes
get {
url: {{baseUrl}}/api/users/{{userId}}
timeout: {{timeout}}
}
headers {
Authorization: Bearer {{authToken}}
X-API-Key: {{apiKey}}
}
Cambiar entornos
# Vía CLI
bru run /path/to/collection --env Development
# Vía interfaz gráfica: Selecciona entorno en dropdown de Bruno
Tipos de variables de entorno
| Tipo | Ejemplo | Uso |
|---|---|---|
| Cadena | "apiKey": "abc123" | {{apiKey}} |
| Número | "timeout": 5000 | {{timeout}} |
| Booleano | "debug": true | {{debug}} |
| Objeto | "config": {...} | Acceso con scripts |
Gestión de secretos
# Crear archivo .env para datos sensibles (agregar a .gitignore)
echo "PROD_API_KEY=secret123" > .env
# Usar en archivo de entorno con referencia
# O usar interfaz de Bruno para marcar campos como "Secreto"
Scripts previos a la solicitud
Agrega JavaScript antes de que se envíe la solicitud:
// Establecer valores dinámicos
bru.setEnvVar('timestamp', Date.now());
bru.setEnvVar('nonce', Math.random().toString(36).substring(7));
// Lógica condicional
if (bru.getEnvVar('env') === 'production') {
bru.setEnvVar('timeout', 10000);
}
// Registrar información de depuración
console.log('Sending request to', bru.getEnvVar('baseUrl'));
Scripts posteriores a la respuesta
Ejecuta JavaScript después de recibir la respuesta:
// Acceder a datos de respuesta
const responseData = res.getBody();
const statusCode = res.getStatus();
const headers = res.getHeaders();
// Guardar valores para siguiente solicitud
if (statusCode === 200) {
bru.setEnvVar('authToken', responseData.token);
bru.setEnvVar('userId', responseData.user.id);
}
// Registrar respuesta
console.log('Status:', statusCode);
console.log('Response:', JSON.stringify(responseData, null, 2));
Operaciones comunes de respuesta
// Obtener código de estado
const status = res.getStatus();
// Obtener cuerpo como cadena
const body = res.getBody();
// Analizar cuerpo JSON
const data = res.getBody(true); // true = analizar como JSON
// Obtener encabezados
const contentType = res.getHeader('content-type');
// Obtener encabezado específico
const authHeader = res.getHeader('authorization');
Aserciones y pruebas
Aserciones de prueba integradas
// Aserción de código de estado
tests['Status is 200'] = (res.getStatus() === 200);
// El cuerpo de respuesta contiene
tests['Response contains user'] = res.getBody().includes('john');
// Validación de respuesta JSON
const data = res.getBody(true);
tests['User ID exists'] = data.user && data.user.id > 0;
// Tiempo de respuesta
tests['Response time < 500ms'] = res.getResponseTime() < 500;
// Validación de encabezado
tests['Content-Type is JSON'] = res.getHeader('content-type').includes('application/json');
Ejemplo de prueba compleja
const data = res.getBody(true);
tests['Status is 201'] = res.getStatus() === 201;
tests['ID is a number'] = typeof data.id === 'number';
tests['Email is valid'] = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(data.email);
tests['Created at is ISO date'] = !isNaN(Date.parse(data.createdAt));
// Guardar para siguiente solicitud
if (tests['Status is 201']) {
bru.setEnvVar('newUserId', data.id);
}
Autenticación
Token portador
auth:bearer {
token: {{authToken}}
}
Autenticación básica
auth:basic {
username: {{username}}
password: {{password}}
}
Clave de API (encabezado)
headers {
X-API-Key: {{apiKey}}
Authorization: ApiKey {{apiKey}}
}
Clave de API (parámetro de consulta)
params:query {
api_key: {{apiKey}}
apiToken: {{token}}
}
OAuth 2.0
auth:oauth2 {
grant_type: authorization_code
authorization_url: https://provider.com/oauth/authorize
token_url: https://provider.com/oauth/token
client_id: {{clientId}}
client_secret: {{clientSecret}}
scope: read write
}
Autenticación de resumen
auth:digest {
username: {{username}}
password: {{password}}
}
Tipos de solicitud y métodos
Solicitud GET
meta {
name: Fetch User
type: http
seq: 1
}
get {
url: {{baseUrl}}/api/users/{{userId}}
}
params:query {
includeProfile: true
fields: id,name,email
}
Solicitud POST
post {
url: {{baseUrl}}/api/users
}
body:json {
{
"name": "Jane Doe",
"email": "jane@example.com"
}
}
Solicitud PUT/PATCH
put {
url: {{baseUrl}}/api/users/{{userId}}
}
body:json {
{
"name": "Jane Smith",
"status": "active"
}
}
Solicitud DELETE
delete {
url: {{baseUrl}}/api/users/{{userId}}
}
Solicitud con encabezados
headers {
Content-Type: application/json
Accept: application/json
User-Agent: Bruno/v1.0
X-Request-ID: {{requestId}}
Authorization: Bearer {{token}}
}
Características avanzadas
Variables a nivel de colección
Define variables en bruno.json:
{
"name": "My API Collection",
"version": "1.0",
"variables": {
"baseUrl": "https://api.example.com",
"version": "v1",
"defaultTimeout": 5000
}
}
Secuenciación de solicitudes
Controla el orden de ejecución de solicitudes en CLI runner:
meta {
name: Authenticate
type: http
seq: 1
}
meta {
name: Get User Data
type: http
seq: 2
}
Las solicitudes se ejecutan en orden de seq.
Consultas GraphQL
meta {
name: GraphQL Query
type: http
seq: 1
}
post {
url: {{baseUrl}}/graphql
}
body:graphql {
query {
user(id: "{{userId}}") {
id
name
email
}
}
}
Parámetros de consulta
params:query {
page: 1
limit: 10
sort: -createdAt
filter: status:active
}
Flujo de trabajo de Git
Por qué nativo de Git es importante
# Las colecciones se almacenan como archivos de texto
.bru/
├── users/
│ ├── get-user.bru
│ └── create-user.bru
└── products/
└── list-products.bru
# Fácil de controlar versiones
git add .
git commit -m "Update API requests"
git push origin main
# Los conflictos de fusión son manejables
# Revisa cambios en PRs
# Colabora con el equipo
Flujo de trabajo de colaboración
# Miembro del equipo clona colección
git clone https://github.com/team/api-collection.git
cd api-collection
# Instala Bruno CLI
npm install -g @usebruno/cli
# Ejecuta pruebas localmente
bru run . --env Development
# Realiza cambios
# Agrega nuevas solicitudes o actualiza existentes
# Haz commit y push
git add .
git commit -m "Add payment API endpoints"
git push origin feature/payments
Ignorar archivos sensibles
# .gitignore en raíz de colección
.env
.env.local
environments/Production.json
!environments/Production.json.example
secrets/
node_modules/
Integración CI/CD
GitHub Actions
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Bruno CLI
run: npm install -g @usebruno/cli
- name: Run API tests
run: bru run . --env CI --reporter json --output test-results.json
- name: Upload results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: test-results.json
GitLab CI
api-tests:
image: node:18
script:
- npm install -g @usebruno/cli
- bru run . --env CI --reporter json --output test-results.json
artifacts:
paths:
- test-results.json
reports:
junit: test-results.json
Hook de pre-commit local
#!/bin/bash
# .git/hooks/pre-commit
echo "Running API tests..."
bru run . --env Development --failOnError
if [ $? -ne 0 ]; then
echo "API tests failed. Commit aborted."
exit 1
fi
Flujos de trabajo comunes
Prueba de API REST
# 1. Configurar entorno
bru run /path/to/collection --env Development
# 2. Verificar resultados de prueba
# Las pruebas muestran aprobado/fallido en salida
# 3. Generar reporte
bru run /path/to/collection --env Development --reporter html --output report.html
Pruebas de carga con solicitudes paralelas
// En script previo a solicitud
for (let i = 0; i < 10; i++) {
bru.setEnvVar('iteration', i);
}
Generación de datos dinámicos
// Generar email único por solicitud
const timestamp = Date.now();
bru.setEnvVar('dynamicEmail', `user_${timestamp}@example.com`);
// Generar ID aleatorio
bru.setEnvVar('randomId', Math.floor(Math.random() * 10000));
Encadenamiento de solicitudes
// En script posterior a respuesta de primera solicitud
const data = res.getBody(true);
bru.setEnvVar('userId', data.id);
// Siguiente solicitud usa {{userId}}
Depuración
Habilitar modo detallado
bru run /path/to/collection --verbose
Ver detalles de solicitud/respuesta
En la interfaz gráfica de Bruno:
- Haz clic en solicitud
- Ver pestaña “Params” para parámetros de consulta
- Ver pestaña “Body” para cuerpo de solicitud
- Ver pestaña “Response” para datos de respuesta
- Ver pestaña “Tests” para resultados de prueba
Registro en consola
// En scripts previos a solicitud o posteriores a respuesta
console.log('Variable value:', bru.getEnvVar('baseUrl'));
console.log('Full response:', res.getBody());
console.log('Status code:', res.getStatus());
Inspección de red
// Verificar encabezados de respuesta
const headers = res.getHeaders();
console.log('All headers:', headers);
// Verificar tiempo de respuesta
console.log('Response time:', res.getResponseTime(), 'ms');
Mejores prácticas de estructura de archivos
api-collection/
├── README.md # Documentación
├── .gitignore # Ignorar archivos sensibles
├── bruno.json # Metadatos de colección
├── environments/ # Archivos de entorno
│ ├── Development.json
│ ├── Staging.json
│ └── Production.json
├── globals.json # Variables globales
├── auth/
│ ├── login.bru
│ └── refresh-token.bru
├── users/
│ ├── get-all-users.bru
│ ├── get-user-by-id.bru
│ ├── create-user.bru
│ ├── update-user.bru
│ └── delete-user.bru
├── products/
│ ├── list-products.bru
│ └── get-product.bru
└── scripts/
├── test-runner.js
└── helpers.js
Recursos
| Recurso | URL |
|---|---|
| Sitio web oficial | usebruno.com |
| Repositorio GitHub | github.com/usebruno/bruno |
| Documentación | docs.usebruno.com |
| Descargar | usebruno.com/downloads |
| Comunidad Discord | discord.gg/usebruno |
| Especificación lenguaje Bru | github.com/usebruno/bru |
| Guía de pruebas de API | docs.usebruno.com/api-testing |
| Problemas de GitHub | github.com/usebruno/bruno/issues |
Consejos y trucos
- Git Diff para revisiones: Como las colecciones son archivos, usa
git diffpara revisar cambios de API antes de fusionar - Plantillas de entorno: Crea archivos
.example.jsonpara entornos para compartir estructura de configuración sin secretos - Scripts reutilizables: Almacena scripts de prueba comunes en archivos
.jsseparados y referénciálos - Alcance de variable: Las variables de colección se aplican globalmente; las variables a nivel de solicitud las sobrescriben
- Rendimiento: Usa bandera
--failOnErroren CI/CD para detectar fallos de prueba temprano - Documentación: Agrega comentarios en archivos
.bruusando sintaxis// comment - Versionamiento: Incluye versión de API en variable de URL base para cambiar fácilmente entre versiones