dandoetech / openapi-generator
Generate OpenAPI 3.1 from DanDoeTech Resource Registry with optional model metadata fallback.
Requires
- php: ^8.2
- dandoetech/resource-registry: ^0.1 || ^0.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.3
This package is auto-updated.
Last update: 2026-04-20 12:25:44 UTC
README
Pre-release — Architecture by senior tech lead, implementation largely AI-assisted with human review. Not fully reviewed. Architecture may change before v1.0.0.
Generate OpenAPI 3.1 specifications from a Resource Registry. Framework-agnostic — no Laravel required.
Installation
composer require dandoetech/openapi-generator
Requires dandoetech/resource-registry.
Quick Start
use DanDoeTech\ResourceRegistry\Registry\{ArrayRegistryDriver, Registry}; use DanDoeTech\ResourceRegistry\Definition\{FieldDefinition, FieldType, ActionDefinition}; use DanDoeTech\OpenApiGenerator\OpenApiGenerator; use DanDoeTech\OpenApiGenerator\Support\ResourceResolver; // Build a registry (or use class-based resources via laravel-resource-registry) $registry = new Registry(new ArrayRegistryDriver([ 'product' => [ 'label' => 'Product', 'fields' => [ new FieldDefinition('id', FieldType::Integer, nullable: false), new FieldDefinition('name', FieldType::String, nullable: false), new FieldDefinition('price', FieldType::Float, nullable: false), ], 'actions' => [new ActionDefinition('create')], ], ])); // Generate the spec $generator = new OpenApiGenerator( resolver: new ResourceResolver(), title: 'My API', version: '1.0.0', ); $doc = $generator->generate($registry); echo $doc->toJson();
Output (abbreviated):
{
"openapi": "3.1.0",
"info": { "title": "My API", "version": "1.0.0" },
"paths": {
"/product": {
"get": { "summary": "List Product", "..." : "..." },
"post": { "summary": "Create Product", "..." : "..." }
},
"/product/{id}": {
"get": { "summary": "Fetch Product", "..." : "..." }
}
},
"components": {
"schemas": {
"Product": {
"type": "object",
"properties": {
"id": { "type": "integer", "format": "int64" },
"name": { "type": "string" },
"price": { "type": "number", "format": "double" }
},
"required": ["id", "name", "price"]
},
"ProblemJson": { "..." : "..." }
}
}
}
Laravel users: Use
dandoetech/laravel-openapi-generatorfor Artisan commands and automatic registry binding.
API Overview
| Class | Purpose |
|---|---|
OpenApiGenerator |
Main entry point — generate(Registry): OpenApiDocument |
OpenApiDocument |
Result container — toArray() and toJson() |
ResourceResolver |
Resolves fields from resource definitions with optional fallback |
PathsBuilder |
Builds OpenAPI path items from resource definitions |
ComponentsBuilder |
Builds component schemas from resource fields |
SchemaFactory |
Maps fields and computed fields to JSON Schema properties |
Types |
Maps FieldType enum to OpenAPI type/format pairs |
Contracts
| Interface | Purpose |
|---|---|
OpenApiGeneratorInterface |
generate(Registry): OpenApiDocument |
ModelMetaProviderInterface |
Optional fallback for field metadata — fieldsFor(string $key): array |
Type Mapping
| FieldType | OpenAPI |
|---|---|
String |
{ "type": "string" } |
Integer |
{ "type": "integer", "format": "int64" } |
Float |
{ "type": "number", "format": "double" } |
Boolean |
{ "type": "boolean" } |
DateTime |
{ "type": "string", "format": "date-time" } |
Json |
{ "type": "object" } |
Date |
{ "type": "string", "format": "date" } |
Text |
{ "type": "string" } |
Email |
{ "type": "string", "format": "email" } |
Url |
{ "type": "string", "format": "uri" } |
Enum |
{ "type": "string" } |
Configuration
All configuration is via constructor arguments:
$generator = new OpenApiGenerator( resolver: new ResourceResolver($optionalFallbackProvider), title: 'My API', version: '2.0.0', servers: [['url' => 'https://api.example.com']], );
Field Fallback
If a resource has no fields defined, the generator can fall back to a ModelMetaProviderInterface:
use DanDoeTech\OpenApiGenerator\ModelMeta\ArrayModelMetaProvider; $fallback = new ArrayModelMetaProvider([ 'product' => [ new FieldDefinition('id', FieldType::Integer, nullable: false), new FieldDefinition('name', FieldType::String, nullable: false), ], ]); $resolver = new ResourceResolver($fallback);
Testing
composer install composer test # PHPUnit composer qa # cs:check + phpstan + test
License
MIT