beauty-framework/openapi-support

1.0.2 2025-06-27 15:09 UTC

This package is not auto-updated.

Last update: 2025-06-27 15:11:23 UTC


README

Modern OpenAPI 3 integration for beauty-framework powered by swagger-php and beautiful Redoc UI out of the box.

Features

  • ⚡️ PSR-7 compatible
  • 🍰 Attribute-based OpenAPI documentation (PHP 8.1+)
  • 🛠 Easy integration in your project
  • 🚀 Out-of-the-box routes for OpenAPI spec and Redoc UI
  • 💾 CLI command for static spec generation

Installation

Require via composer:

composer require beauty-framework/openapi-support

Requirements:

  • PHP >=8.1
  • beauty-framework/cli ^1.0 (for CLI integration)
  • zircote/swagger-php ^5.1 (OpenAPI generator)

Dev Requirements (for testing):

  • phpunit/phpunit ^12.3
  • psr/container ^2.0

Quick Start

  1. Create your API controller inheriting from BaseOpenApiController:
namespace App\Controllers;

use Beauty\OpenApi\Http\Controllers\BaseOpenApiController;
use OpenApi\Attributes as OAT;

#[OAT\OpenApi(openapi: OAT\OpenApi::VERSION_3_1_0, security: [['bearerAuth' => []]])]
#[OAT\Info(
    version: '1.0.0',
    title: 'Basic single file API',
    attachables: [new OAT\Attachable()]
)]
#[OAT\License(name: 'MIT', identifier: 'MIT')]
#[OAT\Server(url: 'https://localhost/', description: 'API server')]
#[OAT\SecurityScheme(securityScheme: 'bearerAuth', type: 'http', scheme: 'bearer', description: 'Basic Auth')]
#[OAT\Tag(name: 'products', description: 'All about products')]
#[OAT\Tag(name: 'catalog', description: 'Catalog API')]
class ApiController extends BaseOpenApiController
{
    // Everything works out of the box!
}
  1. Annotate your endpoints and models using swagger-php attributes:
use OpenApi\Attributes as OAT;

#[OAT\Get(path: '/products', tags: ['products'], ...)]
#[Route(method: HttpMethodsEnum::GET, path: '/products')]
public function listProducts() { ... }
  1. Register console commands (optional, for static generation):

Add to config/commands.php:

return array_merge(
    // ...
    \Beauty\OpenApi\Console\RegisterCommands::commands(),
    // ...
);
  1. Access your docs at:
  • /openapi.json for raw spec
  • /docs/api for interactive Stoplight UI (change between Swagger, Redoc, Rapid, Stoplight via .env OPENAPI_MODE=)
  • You can also provide your own action classes (see SpecsAction and RedocAction) if you want to use custom logic for rendering the spec or documentation page. Just typehint your custom action classes in your controller methods. Example:
  #[Route(method: HttpMethodsEnum::GET, path: '/products')]
  public function openApiJson(MyCustomSpecsAction $action): ResponseInterface
  {
    return $action();
  }
  • This gives you full control over how the OpenAPI spec and documentation UI are served.

Switching Documentation UI

You can control which documentation UI is shown at /docs/api by setting the OPENAPI_MODE variable in your .env file:

OPENAPI_MODE=stoplight # or swagger, redoc, rapid

Supported values:

  • stoplight (default)
  • swagger
  • redoc
  • rapid

The selected viewer will be automatically rendered at /docs/api depending on this value.

If you provide an invalid value, Stoplight will be used by default.

Customization

  • Override redoc() or openApiJson() methods in your controller to change the output.
  • Redoc page can be replaced or extended as needed.

How it works

  • At runtime, SpecsAction uses OpenApiGenerator to scan your codebase and build the OpenAPI spec.
  • RedocAction renders a simple Redoc UI page pointing to your /openapi.json.
  • For production, you can pre-generate the spec and serve it as a static file for performance.

Advanced: Static Generation

You can generate the spec as a file using the CLI command:

./beauty openapi:generate

This allows serving the OpenAPI spec statically in production.

Under the Hood

  • Built on zircote/swagger-php — the de-facto OpenAPI generator for PHP.
  • Fully compatible with beauty/http responses.

Limitations & Tips

  • Static vs. Dynamic: If you have a static /public/openapi.json, RoadRunner will serve it as a file instead of going through the controller. Remove the file if you want dynamic generation.
  • Performance: Generating the spec dynamically on every request can be slow on large projects. Consider using static generation for production.
  • Customization: For advanced Redoc pages, simply override the redoc() method.

Links

License

MIT