kleijnweb/php-api-descriptions
Parse and handle PHP API Description documents (OpenAPI-like) with utilities for loading, validating, and working with structured API metadata. Useful for tooling that needs to read API specs and generate clients, docs, or integrations.
Installation Add the package via Composer:
composer require kleijnweb/php-api-descriptions
Ensure your project uses PHP 8.0+ (recommended) and Laravel 8+.
First Use Case: Defining an API Contract
Create a Contract class (e.g., app/Contracts/UserContract.php):
use Kleijnweb\ApiDescriptions\Contract;
class UserContract extends Contract
{
public function getDescription(): string
{
return 'API for user management';
}
public function getEndpoints(): array
{
return [
'GET /users' => $this->getUsers(),
'POST /users' => $this->createUser(),
];
}
public function getUsers(): string
{
return 'Returns a list of users';
}
public function createUser(): string
{
return 'Creates a new user';
}
}
Generating Documentation
Use the ApiDescriptions facade to generate OpenAPI/Swagger docs:
use Kleijnweb\ApiDescriptions\Facades\ApiDescriptions;
$contract = new UserContract();
$description = ApiDescriptions::describe($contract);
Viewing Output Output the description as JSON or YAML:
$json = $description->toJson();
$yaml = $description->toYaml();
Define Contracts Early Create contracts before implementing endpoints. Example:
class OrderContract extends Contract
{
public function getEndpoints(): array
{
return [
'GET /orders/{id}' => $this->getOrder(),
'POST /orders' => $this->createOrder(),
];
}
public function getOrder(): string
{
return 'Returns an order by ID';
}
public function createOrder(): array
{
return [
'description' => 'Creates an order',
'requestBody' => [
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'properties' => [
'product_id' => ['type' => 'integer'],
'quantity' => ['type' => 'integer'],
],
],
],
],
],
];
}
}
Integrate with Laravel Routes Use contracts to validate routes dynamically:
Route::get('/users', function () {
$contract = new UserContract();
$description = ApiDescriptions::describe($contract);
// Use $description to validate requests or generate docs
});
Group Contracts by Module
Organize contracts by feature (e.g., app/Contracts/Auth/, app/Contracts/Ecommerce/).
Merge them in a central ApiContract:
class ApiContract extends Contract
{
public function getEndpoints(): array
{
return array_merge(
(new AuthContract())->getEndpoints(),
(new EcommerceContract())->getEndpoints()
);
}
}
Generate API Docs Automatically Create a Laravel command to dump OpenAPI specs:
use Kleijnweb\ApiDescriptions\Facades\ApiDescriptions;
use Illuminate\Console\Command;
class GenerateApiDocs extends Command
{
protected $signature = 'api:docs';
protected $description = 'Generate OpenAPI documentation';
public function handle()
{
$contract = new ApiContract();
$description = ApiDescriptions::describe($contract);
file_put_contents(public_path('api-docs.yaml'), $description->toYaml());
$this->info('API docs generated!');
}
}
Dynamic Endpoint Descriptions Use closures for dynamic descriptions:
public function getEndpoints(): array
{
return [
'GET /products' => fn() => 'Returns ' . config('app.env') . ' products',
];
}
Reuse Descriptions Extend contracts to share common endpoints:
class BaseContract extends Contract
{
protected function healthCheck(): string
{
return 'Returns API health status';
}
}
class AdminContract extends BaseContract
{
public function getEndpoints(): array
{
return [
'GET /health' => $this->healthCheck(),
];
}
}
Integrate with Laravel Validation Use contract descriptions to validate requests:
use Kleijnweb\ApiDescriptions\Description;
Route::post('/users', function (Request $request) {
$contract = new UserContract();
$description = ApiDescriptions::describe($contract);
$endpoint = $description->getEndpoint('POST /users');
if ($endpoint['requestBody']) {
$validator = Validator::make($request->all(), $endpoint['requestBody']['content']['application/json']['schema']['properties']);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
}
});
Archived Package
zircote/swagger-php if long-term maintenance is critical.Limited OpenAPI Support
Description class:
$description->setServers(['https://api.example.com']);
$description->addSecurityScheme('api_key', ['type' => 'apiKey', 'in' => 'header']);
No Built-in Route Registration
// ❌ Won't work (contracts don't auto-register)
// $contract->getEndpoints();
// ✅ Correct: Manually map routes
Route::get('/users', [UserController::class, 'index']);
Performance with Large APIs
$description = Cache::remember('api-docs', now()->addHours(1), function () {
return ApiDescriptions::describe(new ApiContract());
});
Validate Descriptions
Use the toArray() method to inspect raw data:
$description = ApiDescriptions::describe(new UserContract());
dd($description->toArray());
Handle Missing Endpoints
If an endpoint is missing, the package throws a RuntimeException. Catch it gracefully:
try {
$endpoint = $description->getEndpoint('GET /nonexistent');
} catch (\RuntimeException $e) {
Log::warning('Missing endpoint: ' . $e->getMessage());
}
Extend Description Class Override methods in a custom class for additional fields:
class CustomDescription extends \Kleijnweb\ApiDescriptions\Description
{
public function addCustomField(string $key, $value): self
{
$this->data['x-' . $key] = $value;
return $this;
}
}
No Built-in Config File The package has no default config. Initialize it manually:
ApiDescriptions::setTitle('My API')
->setVersion('1.0.0')
->setDescription('API for my Laravel app');
YAML Output Formatting
The toYaml() method uses basic formatting. For pretty-printing, use a library like spatie/fork:
use Spatie\ArrayToXml\ArrayToXml;
$yaml = ArrayToXml::convertToYaml($description->toArray(), [], true);
PHP 8.0+ Features The package leverages named arguments and union types. Ensure your PHP version supports them:
// Works in PHP 8.0+
public function getEndpoints(): array
{
return [
'GET /users' => $this->getUsers(description: 'List all users'),
];
}
Custom Description Formats
Extend the Description class to support new formats (e.g., Markdown):
class MarkdownDescription extends \Kleijnweb\ApiDescriptions\Description
{
public function toMarkdown(): string
{
return "# API Description\n\n" . $this->data['description'];
}
}
**Integrate with API Gate
How can I help you explore Laravel packages today?