spatie/laravel-openapi-cli
Generate Laravel Artisan commands from an OpenAPI spec. Each API endpoint becomes its own command with typed options for params and request bodies, plus auth, base URL, caching, redirects, and output formatting—ideal for building API CLIs with Laravel Zero.
Install the package:
composer require spatie/laravel-openapi-cli
Publish the config (if needed):
php artisan vendor:publish --provider="Spatie\OpenApiCli\OpenApiCliServiceProvider"
Register an API spec in a service provider:
use Spatie\OpenApiCli\Facades\OpenApiCli;
OpenApiCli::register('https://api.example.com/openapi.yaml', 'example-api')
->baseUrl('https://api.example.com');
Run the command generator:
php artisan openapi:generate
This creates Artisan commands for each endpoint in your OpenAPI spec (e.g., example-api:get-users).
Use the generated command:
php artisan example-api:get-users --limit=10
For a GET /users endpoint with query params limit and offset, the package generates:
php artisan example-api:get-users --limit=5 --offset=10
No manual argument parsing or HTTP client setup required.
Register APIs:
Use OpenApiCli::register() in a service provider (e.g., AppServiceProvider@boot) to map OpenAPI specs to CLI namespaces. Configure auth, base URLs, and caching per API.
Generate Commands:
Run php artisan openapi:generate to create commands. Regenerate after spec updates:
php artisan openapi:generate --force
Customize Commands: Extend generated commands by publishing stubs:
php artisan vendor:publish --tag="openapi-cli.stubs"
Override app/Commands/OpenApiCli/ to add pre/post hooks (e.g., logging, input validation).
Integrate with Laravel Zero:
Register APIs in AppServiceProvider and use commands directly in a Laravel Zero app:
$this->command('example-api:get-users', function () {
return $this->call('example-api:get-users', ['--limit' => 10]);
});
Dynamic API Registration: Load specs from environment variables or config:
OpenApiCli::register(config('services.api.spec'), config('services.api.namespace'));
Multi-Tenant APIs: Use a factory to register tenant-specific APIs:
OpenApiCli::register(
"https://api.tenant-{tenant_id}.com/openapi.yaml",
"tenant-{tenant_id}"
)->baseUrl("https://api.tenant-{tenant_id}.com");
Reusable Auth Logic: Centralize auth in a trait or service:
OpenApiCli::register('...')
->auth(fn () => $this->getAuthToken());
Command Chaining: Chain commands for workflows (e.g., fetch data then process it):
php artisan example-api:get-users --limit=10 | php artisan process-users
Laravel HTTP Client: The package uses Laravel’s HTTP client under the hood. Customize it via config:
'http' => [
'timeout' => 30,
'headers' => [
'Accept' => 'application/json',
],
],
Event Listeners:
Listen to OpenApiCli.Generated to log or validate generated commands:
event(new OpenApiCliGenerated($commands));
Testing: Mock the facade in tests:
OpenApiCli::shouldReceive('register')->once();
Spec Parsing Errors:
paths or malformed schemas) cause silent failures.--verbose flag:
php artisan openapi:generate --verbose
Parameter Name Collisions:
/users/{id}) and query params with the same name (e.g., ?id=1) may conflict.-- for query params explicitly:
php artisan example-api:get-user --id=123 --query-id=456
Caching Quirks:
php artisan cache:clear
Or disable caching during development:
->cache(ttl: 0)
Auth Token Expiry:
retryOn hook to refresh tokens:
->retryOn(function ($response) {
return $response->status() === 401;
}, maxRetries: 3)
Command Naming Conflicts:
api-v2:) or override the getCommandName() method in stubs.Inspect Generated Commands:
Check app/Commands/OpenApiCli/ for auto-generated files. Override them to debug logic.
HTTP Request Logging: Enable Laravel’s HTTP client logging:
'http' => [
'debug' => env('APP_DEBUG', false),
],
Spec Diffing:
Compare specs before/after updates using diff:
curl -s https://api.example.com/openapi.yaml > old.yaml
php artisan openapi:generate
curl -s https://api.example.com/openapi.yaml > new.yaml
diff old.yaml new.yaml
Custom Command Classes:
Publish stubs and extend Spatie\OpenApiCli\Commands\OpenApiCommand to add:
Dynamic Spec Loading:
Override Spatie\OpenApiCli\OpenApiCli::resolveSpec() to load specs from databases or S3:
public static function resolveSpec(string $spec): string
{
return Storage::disk('s3')->get("openapi/{$spec}.yaml");
}
Output Formatters:
Extend the Spatie\OpenApiCli\Commands\OpenApiCommand to support custom output (e.g., CSV, Markdown):
->outputFormatter(function ($response) {
return response()->json($response->json());
});
Operation ID Overrides:
Use operationId in the OpenAPI spec to control command names. For custom IDs:
paths:
/users:
get:
operationId: custom-get-users-list
Laravel Zero + OpenAPI: Combine with Laravel Zero to build standalone CLI tools:
// In a Laravel Zero app
OpenApiCli::register('https://api.example.com/openapi.yaml', 'api')
->banner('My CLI Tool')
->useOperationIds();
Interactive Mode:
Use --interactive to prompt for missing parameters:
php artisan example-api:post-users --interactive
Rate Limit Handling:
Leverage the onError hook to handle rate limits gracefully:
->onError(function ($response, $command) {
if ($response->status() === 429) {
$command->warn('Rate limited. Retry after ' . $response->header('Retry-After') . 's.');
return true; // Retry automatically
}
return false;
})
Environment-Specific Configs: Use different specs/configs per environment:
if (app()->environment('staging')) {
OpenApiCli::register('https://staging.api.example.com/openapi.yaml', 'staging-api');
}
How can I help you explore Laravel packages today?