| name | bruno-collection-generator |
| description | Generates Bruno collection files (.bru) from Express, Next.js, Fastify, or other API routes. Creates organized collections with environments, authentication, and folder structure for the open-source Bruno API client. Use when users request "generate bruno collection", "bruno api testing", "create bru files", or "bruno import". |
Bruno Collection Generator
Generate Bruno collection files for the open-source, Git-friendly API client.
Core Workflow
- Scan routes: Find all API route definitions
- Extract metadata: Methods, paths, params, bodies
- Create collection: Initialize bruno.json manifest
- Generate .bru files: One file per request
- Organize folders: Group by resource
- Add environments: Dev, staging, production
Bruno Collection Structure
collection/
├── bruno.json # Collection manifest
├── environments/
│ ├── Development.bru
│ ├── Staging.bru
│ └── Production.bru
├── users/
│ ├── folder.bru
│ ├── get-users.bru
│ ├── get-user.bru
│ ├── create-user.bru
│ ├── update-user.bru
│ └── delete-user.bru
├── auth/
│ ├── folder.bru
│ ├── login.bru
│ ├── register.bru
│ └── logout.bru
└── products/
├── folder.bru
└── ...
bruno.json Manifest
{
"version": "1",
"name": "My API",
"type": "collection",
"ignore": ["node_modules", ".git"]
}
.bru File Syntax
meta {
name: Get Users
type: http
seq: 1
}
get {
url: {{baseUrl}}/users
body: none
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
query {
page: 1
limit: 10
}
headers {
Accept: application/json
}
docs {
Retrieve a paginated list of users.
}
Generator Script
import * as fs from "fs";
import * as path from "path";
interface RouteInfo {
method: string;
path: string;
name: string;
description?: string;
body?: object;
queryParams?: { name: string; value: string }[];
auth?: boolean;
}
interface BrunoOptions {
collectionName: string;
outputDir: string;
baseUrl: string;
authType?: "bearer" | "basic" | "apikey";
}
function generateBrunoCollection(
routes: RouteInfo[],
options: BrunoOptions
): void {
const { outputDir, collectionName } = options;
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { : });
}
manifest = {
: ,
: collectionName,
: ,
: [, ],
};
fs.(
path.(outputDir, ),
.(manifest, , )
);
(outputDir, options);
groupedRoutes = (routes);
( [resource, resourceRoutes] .(groupedRoutes)) {
folderPath = path.(outputDir, resource);
(!fs.(folderPath)) {
fs.(folderPath, { : });
}
folderBru = ;
fs.(path.(folderPath, ), folderBru);
seq = ;
( route resourceRoutes) {
fileName = (route);
content = (route, seq++, options);
fs.(path.(folderPath, ), content);
}
}
}
(): {
: [] = [];
lines.();
lines.();
lines.();
lines.();
lines.();
lines.();
method = route..();
urlPath = route..(, );
lines.();
lines.();
([, , ].(method) && route.) {
lines.();
} {
lines.();
}
(route. && options.) {
lines.();
} {
lines.();
}
lines.();
lines.();
(route. && options. === ) {
lines.();
lines.();
lines.();
lines.();
} (route. && options. === ) {
lines.();
lines.();
lines.();
lines.();
lines.();
}
(route.?.) {
lines.();
( param route.) {
lines.();
}
lines.();
lines.();
}
lines.();
lines.();
([, , ].(method)) {
lines.();
}
lines.();
lines.();
([, , ].(method) && route.) {
lines.();
lines.(.(route., , ));
lines.();
lines.();
}
(route.) {
lines.();
lines.();
lines.();
}
lines.();
}
(): {
envsDir = path.(outputDir, );
(!fs.(envsDir)) {
fs.(envsDir, { : });
}
environments = [
{ : , : },
{ : , : },
{ : , : },
];
( env environments) {
content = ;
fs.(path.(envsDir, ), content);
}
}
(): {
route..().(, );
}
(): <, []> {
: <, []> = {};
( route routes) {
parts = route..().();
resource = parts[] || ;
(!groups[resource]) {
groups[resource] = [];
}
groups[resource].(route);
}
groups;
}
(): {
str.().() + str.();
}
Complete Example Files
bruno.json
{
"version": "1",
"name": "My API",
"type": "collection",
"ignore": ["node_modules", ".git"]
}
environments/Development.bru
vars {
baseUrl: http://localhost:3000/api
authToken:
userId: 1
}
vars:secret [
authToken
]
environments/Production.bru
vars {
baseUrl: https://api.example.com
authToken:
userId:
}
vars:secret [
authToken
]
users/folder.bru
meta {
name: Users
}
users/get-users.bru
meta {
name: Get Users
type: http
seq: 1
}
get {
url: {{baseUrl}}/users
body: none
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
query {
page: 1
limit: 10
}
headers {
Accept: application/json
}
docs {
Retrieve a paginated list of users.
## Query Parameters
- page: Page number (default: 1)
- limit: Items per page (default: 10, max: 100)
## Response
Returns paginated user list with metadata.
}
users/get-user.bru
meta {
name: Get User by ID
type: http
seq: 2
}
get {
url: {{baseUrl}}/users/{{userId}}
body: none
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
headers {
Accept: application/json
}
docs {
Retrieve a single user by their ID.
}
users/create-user.bru
meta {
name: Create User
type: http
seq: 3
}
post {
url: {{baseUrl}}/users
body: json
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
headers {
Accept: application/json
Content-Type: application/json
}
body:json {
{
"name": "John Doe",
"email": "john@example.com",
"role": "user"
}
}
docs {
Create a new user account.
## Request Body
- name: User's full name (required)
- email: User's email address (required, unique)
- role: User role (optional, default: "user")
}
users/update-user.bru
meta {
name: Update User
type: http
seq: 4
}
put {
url: {{baseUrl}}/users/{{userId}}
body: json
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
headers {
Accept: application/json
Content-Type: application/json
}
body:json {
{
"name": "John Updated",
"email": "john.updated@example.com"
}
}
docs {
Update an existing user.
}
users/delete-user.bru
meta {
name: Delete User
type: http
seq: 5
}
delete {
url: {{baseUrl}}/users/{{userId}}
body: none
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
headers {
Accept: application/json
}
docs {
Delete a user account.
}
auth/login.bru
meta {
name: Login
type: http
seq: 1
}
post {
url: {{baseUrl}}/auth/login
body: json
auth: none
}
headers {
Accept: application/json
Content-Type: application/json
}
body:json {
{
"email": "user@example.com",
"password": "password123"
}
}
script:post-response {
if (res.body.token) {
bru.setEnvVar("authToken", res.body.token);
}
}
docs {
Authenticate user and receive access token.
On successful login, the token is automatically saved
to the authToken environment variable.
}
Pre/Post Request Scripts
script:pre-request {
// Set dynamic values before request
const timestamp = Date.now();
bru.setVar("requestId", `req-${timestamp}`);
}
script:post-response {
// Extract values from response
if (res.body.token) {
bru.setEnvVar("authToken", res.body.token);
}
if (res.body.id) {
bru.setEnvVar("userId", res.body.id);
}
// Log response info
console.log(`Status: ${res.status}`);
console.log(`Response time: ${res.responseTime}ms`);
}
Tests in Bruno
tests {
test("should return 200", function() {
expect(res.status).to.equal(200);
});
test("should return array of users", function() {
expect(res.body.data).to.be.an("array");
});
test("should include pagination", function() {
expect(res.body.meta).to.have.property("page");
expect(res.body.meta).to.have.property("total");
});
}
CLI Script
#!/usr/bin/env node
import * as fs from "fs";
import { program } from "commander";
program
.name("bruno-gen")
.description("Generate Bruno collection from API routes")
.option("-f, --framework <type>", "Framework type", "express")
.option("-s, --source <path>", "Source directory", "./src")
.option("-o, --output <path>", "Output directory", "./bruno-collection")
.option("-n, --name <name>", "Collection name", "My API")
.option("-b, --base-url <url>", "Base URL", "http://localhost:3000/api")
.option("-a, --auth <type>", "Auth type (bearer|basic|apikey)")
.parse();
const options = program.opts();
async function main() {
const routes = await scanRoutes(options.framework, options.source);
generateBrunoCollection(routes, {
: options.,
: options.,
: options.,
: options.,
});
.();
.();
}
();
Best Practices
- Git-friendly: Bruno stores everything as plain text files
- Use environments: Store URLs and tokens in environment files
- Secret variables: Mark sensitive vars with
vars:secret
- Add docs: Document each request with the docs block
- Pre/post scripts: Automate token extraction and setup
- Add tests: Include assertions in test blocks
- Organize folders: Group related requests together
- Sequence numbers: Order requests logically with
seq
Output Checklist