Generate OpenAPI 3.0 specs from Bruno API collections.
bru2openapi takes a Bruno collection directory, runs every request through a capture proxy, and converts the recorded traffic into an OpenAPI specification using mitmproxy2swagger.
flowchart LR
A[Bruno Collection] -->|bru run| B[mitmdump proxy]
B -->|captures traffic| C[flows.mitm]
C -->|mitmproxy2swagger| D[openapi.yaml]
- Reads the base URL from your Bruno environment file
- Starts
mitmdumpas a capture proxy - Runs all collection requests through the proxy via the
bruCLI - Converts captured traffic to HAR format
- Generates an OpenAPI spec with
mitmproxy2swagger
Requires Node.js 22+.
npm install -g bru2openapi# Docker mode (default) -- only requires Docker
bru2openapi ./my-collection --env Development --base-url-var BaseUrl
# Local mode -- requires mitmproxy, mitmproxy2swagger, and bru CLI
bru2openapi ./my-collection --env Development --base-url-var BaseUrl --localIf you omit required flags, the CLI will prompt you interactively.
bru2openapi <collection-dir> [flags]
ARGUMENTS
collection-dir Path to Bruno collection directory
FLAGS
-e, --env <name> Bruno environment name
-b, --base-url-var <name> Name of the env variable holding the base URL
--format json|xml Expected API response format (default: json)
--[no-]get-only Only run GET requests (default: true)
--include <folders> Comma-separated folders to include, or ALL (default: ALL)
--exclude <folders> Comma-separated folders to exclude
--out <dir> Output directory (default: .)
--local Run locally instead of Docker
-y, --yes Skip confirmation prompts
Only requires Docker. The CLI builds and caches a Docker image (bru2openapi:<version>) containing mitmproxy, mitmproxy2swagger, and the Bruno CLI. The image is rebuilt automatically when the CLI version changes.
bru2openapi ./collection -e Production -b ApiBaseUrlRuns the pipeline directly on your machine. You need to install the dependencies yourself:
pip install mitmproxy mitmproxy2swagger
npm i -g @usebruno/cliThen:
bru2openapi ./collection -e Production -b ApiBaseUrl --localThe CLI checks for all three dependencies at startup and tells you what to install if anything is missing.
Place a .bru2openapi.yml in your collection root to save per-collection settings:
env: Development
baseUrlVar: BaseUrl
format: xml
getOnly: true
include:
- Stores
- Products
forceArrayJPaths:
- Stores
- Order.Payments
- PersonsCLI flags take precedence over the config file, which takes precedence over interactive prompts, which fall back to built-in defaults:
CLI flags > .bru2openapi.yml > interactive prompts > defaults
When format: xml, XML responses are converted to JSON before schema generation. XML-to-JSON conversion can lose array semantics when a collection contains only one element. The forceArrayJPaths list tells the converter which jPaths are always arrays, using dot-separated paths from the document root (e.g. Order.Payments).
Array jPaths are also auto-discovered via two heuristics:
- Parent element has
@Type="Array"attribute - Multiple same-name sibling elements exist in the raw XML
Limit which collection folders are run. Accepts a comma-separated list or the keyword ALL:
# Only run Stores and Products folders
bru2openapi ./collection -e Dev -b BaseUrl --include Stores,Products
# Run all folders (override a restrictive config file)
bru2openapi ./collection -e Dev -b BaseUrl --include ALLALL cannot be combined with specific folder names (e.g. --include ALL,Stores is an error).
Skip specific folders:
bru2openapi ./collection -e Dev -b BaseUrl --exclude Returns,Coupons--include and --exclude are mutually exclusive.
When run in a terminal without all required flags, the CLI prompts for missing values with defaults pre-filled from the config file:
? Bruno environment (Development):
? Base URL variable (BaseUrl):
? Response format (json):
? GET requests only (Y/n):
? Include folders (ALL):
? Output directory (.):
? Run locally (no Docker) (y/N):
Press Enter to accept the default or type a new value.
Use --yes / -y to skip all prompts (for CI/scripts). If required values are still missing, the CLI exits with an error.
The CLI produces these files in the output directory:
| File | Description |
|---|---|
<collection-name>-openapi.yml |
The generated OpenAPI spec |
flows.mitm |
Raw mitmproxy capture file |
flows.har |
HAR file (converted from flows) |
requests-report.json |
Execution log with failures and array jPath info |
The generated spec is a starting point. Consider these improvements:
- Add descriptions to paths and parameters
- Add tags to group endpoints
- Add security schemes (e.g. Basic Auth)
- Parameterise path variables (replace hardcoded IDs with
{paramName}) - Add error responses (400, 401, 404, 500)
- Validate:
npx @redocly/cli lint <spec-file>
pnpm install
pnpm testISC