Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion config/api-toolkit.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
| OpenAPI
|--------------------------------------------------------------------------
|
| Configuration for the OpenAPI spec generator.
| Configuration for the OpenAPI 3.1 spec generator.
| https://www.openapis.org/
|
| Run: php artisan api-toolkit:openapi
|
*/
Expand Down Expand Up @@ -78,4 +80,32 @@
// ],
],

/*
|--------------------------------------------------------------------------
| Bruno
|--------------------------------------------------------------------------
|
| Configuration for the Bruno API client collection generator.
| https://www.usebruno.com/
|
| Run: php artisan api-toolkit:bruno
|
*/

'bruno' => [
'name' => env('APP_NAME', 'API'),
'output' => base_path('bruno'),
'base_url' => '{{host}}',

// Define multiple collections for versioned APIs.
// When set, the generator creates a separate Bruno collection per entry.
'collections' => [
// 'v1' => [
// 'name' => 'API v1',
// 'prefix' => 'v1',
// 'output' => app_path('V1/Bruno'),
// ],
],
],

];
3 changes: 2 additions & 1 deletion docs/1.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ API Toolkit is a Laravel package that gives you everything you need to build [JS
- **Pagination** - Offset and cursor-based pagination with configurable page sizes
- **Error Handling** - Automatic exception rendering in JSON:API format, including validation errors with field pointers
- **OpenAPI Generation** - Auto-generate OpenAPI 3.1 specs from your routes and resources
- **Bruno Collection** - Generate Bruno API client collections with pre-filled request bodies
- **Testing Utilities** - Chainable assertions purpose-built for JSON:API responses
- **Middleware** - Force JSON:API content type on all responses
- **Middleware** - Force JSON:API content type and ETag caching on responses

## Why API Toolkit?

Expand Down
184 changes: 184 additions & 0 deletions docs/3.advanced/7.bruno.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: Bruno Collection
description: Generate Bruno API client collections from your routes.
---

# Bruno Collection

The toolkit can generate a [Bruno](https://www.usebruno.com/) collection from your API routes. Bruno is a fast, open-source API client that stores collections as plain files, making them easy to version control alongside your code.

## Generating the Collection

```bash
php artisan api-toolkit:bruno
```

This scans your routes, detects endpoints that use API Toolkit resources, and generates `.bru` files organized by resource.

### Options

```bash
# Custom output directory
php artisan api-toolkit:bruno --output=app/V1/Bruno
```

## Generated Structure

```
bruno/
bruno.json # Collection metadata
collection.bru # Default bearer auth
environments/
Local.bru # Host and token variables
Products/
List.bru # GET /products
Create.bru # POST /products
View.bru # GET /products/{product}
Update.bru # PUT/POST /products/{product}
Delete.bru # DELETE /products/{product}
Orders/
Items/ # Nested resources get subfolders
List.bru # GET /orders/{order}/items
Create.bru # POST /orders/{order}/items
```

### What Gets Generated

- **Request files** - One `.bru` file per HTTP method per resource, with the correct URL and method
- **URL variables** - Route parameters are converted to Bruno environment variables (e.g. `{{productId}}`)
- **Post-response scripts** - List and Create endpoints automatically capture the resource ID into an environment variable for use in subsequent requests
- **Pre-request scripts** - POST/PUT/PATCH endpoints include a `req.setBody()` script with fields from your FormRequest rules
- **Environment files** - A `Local.bru` file with `host` and `apiToken` variables
- **Secret variables** - All captured resource IDs are registered as `vars:secret` in environment files

## Pre-Request Body Generation

The generator reads your FormRequest `rules()` method to build the request body. Field values are determined by:

1. **`@example` docblocks** - Add above a field rule to set a specific example value
2. **`_id` fields** - Automatically use `bru.getEnvVar()` to reference captured IDs
3. **Rule-based guessing** - `email` rules get `email@example.com`, `integer` gets `0`, `boolean` gets `true`
4. **Fallback** - Empty string `''`

### Using @example Docblocks

Add `@example` annotations above your form request rules to provide meaningful example values:

```php
use BlueBeetle\ApiToolkit\Http\Requests\FormRequest;

class CreateProductRequest extends FormRequest
{
public function rules(): array
{
return [
/**
* @example Widget Pro
*/
'name' => ['required', 'string', 'min:3'],
/**
* @example PRD-001
*/
'code' => ['required', 'string'],
'category_id' => ['required', 'exists:categories,id'],
/**
* @example 2999
*/
'price_in_cents' => ['required', 'integer', 'min:0'],
];
}
}
```

This generates:

```javascript
script:pre-request {
req.setBody({
name: 'Widget Pro',
code: 'PRD-001',
category_id: bru.getEnvVar('categoryId'),
price_in_cents: '2999',
});
}
```

### Nested Fields

Dot-notation fields in your rules (e.g. `billing.currency_id`) are automatically grouped into nested objects:

```javascript
req.setBody({
billing: {
currency_id: bru.getEnvVar('currencyId'),
payment_days: 'immediately',
},
});
```

## Action Routes

Routes with action segments like `/restore`, `/archive`, or `/approve` are detected automatically and generate their own `.bru` files:

```
Products/
Restore.bru # POST /products/{product}/restore
Archive.bru # POST /products/{product}/archive
```

## Configuration

In `config/api-toolkit.php`:

```php
'bruno' => [
'name' => env('APP_NAME', 'API'),
'output' => base_path('bruno'),
'base_url' => '{{host}}',
],
```

| Option | Default | Description |
|---|---|---|
| `name` | `APP_NAME` | Collection name in `bruno.json` |
| `output` | `base_path('bruno')` | Output directory |
| `base_url` | `{{host}}` | Base URL prepended to all request paths |

## Versioned APIs

For APIs with multiple versions, define collections per version:

```php
'bruno' => [
'name' => env('APP_NAME', 'API'),
'base_url' => '{{host}}',
'collections' => [
'v1' => [
'name' => 'API v1',
'prefix' => 'v1',
'output' => app_path('V1/Bruno'),
],
'v2' => [
'name' => 'API v2',
'prefix' => 'v2',
'output' => app_path('V2/Bruno'),
],
],
],
```

Each collection gets its own `bruno.json`, `collection.bru`, environment files, and resource folders. The `prefix` matches against the route URL path to filter endpoints per collection.

```bash
php artisan api-toolkit:bruno # generates all configured collections
```

The `--output` flag overrides the multi-collection config and generates a single collection.

## Regeneration

Running the command again:

- **Deletes and recreates** all resource folders (removes stale endpoints)
- **Preserves** `bruno.json`, `collection.bru`, and environment files
- **Updates** the `vars:secret` block in all environment files with current resource IDs
2 changes: 2 additions & 0 deletions src/ApiToolkitServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace BlueBeetle\ApiToolkit;

use BlueBeetle\ApiToolkit\Console\GenerateBrunoCommand;
use BlueBeetle\ApiToolkit\Console\GenerateOpenApiCommand;
use BlueBeetle\ApiToolkit\Console\MakeResourceCommand;
use BlueBeetle\ApiToolkit\Http\Response;
Expand Down Expand Up @@ -36,6 +37,7 @@ public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
GenerateBrunoCommand::class,
GenerateOpenApiCommand::class,
MakeResourceCommand::class,
]);
Expand Down
Loading