nicoandra / laravel-data-openapi-generator
Generate OpenAPI specification from Laravel routes and Laravel Data objects.
Package info
github.com/nicoandra/laravel-data-openapi-generator
pkg:composer/nicoandra/laravel-data-openapi-generator
Requires
- laravel/framework: ^8|^9|^10|^11
- phpdocumentor/reflection-docblock: ^5.4
- spatie/invade: ^2
- spatie/laravel-data: ^4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.59
- larastan/larastan: ^3.9
- orchestra/testbench: ^9
- pestphp/pest: ^v2.34
This package is auto-updated.
Last update: 2026-05-05 19:24:06 UTC
README
Generate an OpenAPI specification directly from Laravel routes and Spatie Laravel Data classes.
The package treats your controllers and Data objects as the source of truth for request and response documentation, so your OpenAPI file stays close to the code that actually handles the API.
What it does
- Generates OpenAPI paths from Laravel routes.
- Infers request bodies and response schemas from
Dataclasses. - Supports route parameters declared directly in controller methods.
- Supports route parameters injected through
#[FromRouteParameter(...)]. - Reads
#[Summary],#[Description], and#[Example]attributes to enrich the generated spec. - Supports
#[IgnoreFromOpenApi]on request properties, controller classes, and controller methods, to keep utility methods from being exposed to the public. - Supports custom response status codes through
#[HttpResponseStatus(...)]. - Supports multiple content types through
#[CustomContentType(...)]. - Adds security requirements and
401/403responses from configured middleware. - Allows namespace aliasing so generated schema names do not leak internal class structure.
- Exposes both a JSON endpoint and a Swagger UI page.
Installation
Install the package:
composer require nicoandra/laravel-data-openapi-generator
Publish the configuration:
php artisan vendor:publish --tag=openapi-generator-config
Quick start
Use Data classes for your API inputs and outputs:
<?php namespace App\Data; use NicoAndra\OpenApiGenerator\Attributes\Description; use NicoAndra\OpenApiGenerator\Attributes\Example; use NicoAndra\OpenApiGenerator\Attributes\IgnoreFromOpenApi; use Spatie\LaravelData\Attributes\FromRouteParameter; use Spatie\LaravelData\Data; class CreatePostData extends Data { public function __construct( #[FromRouteParameter('author')] public int $authorId, #[Example('How to keep docs in sync with code')] public string $title, #[IgnoreFromOpenApi] public int $tenantId, public string $body, ) {} } #[Description('Created post')] class PostData extends Data { public function __construct( public int $id, public string $title, ) {} }
Annotate the controller method:
<?php namespace App\Http\Controllers; use App\Data\CreatePostData; use App\Data\PostData; use NicoAndra\OpenApiGenerator\Attributes\Description; use NicoAndra\OpenApiGenerator\Attributes\Summary; class PostController { #[Summary('Create a post')] #[Description('Creates a post for an author and returns the stored resource')] public function store(CreatePostData $data): PostData { // ... } }
Register a route:
Route::post('/api/authors/{author}/posts', [PostController::class, 'store']);
Generate the OpenAPI file:
php artisan openapi:generate
By default the generated file is written to resources/api/openapi.json.
Generated routes
The package registers two routes:
GET /api/openapirenders Swagger UI.GET /api/openapi.jsonreturns the generated OpenAPI JSON.
Generate the file first with:
php artisan openapi:generate
Supported attributes
#[Summary(...)]
Attach to a controller method to populate the OpenAPI operation summary.
#[Description(...)]
Attach to a controller method to describe the operation.
Attach to a Data class to describe the response associated with that class.
#[Example(...)]
Attach to a Data property or controller parameter to add example values to the generated schema or parameter.
#[IgnoreFromOpenApi]
Attach to a request Data property to exclude it from generated request bodies and derived GET query parameters.
Attach to a controller class or controller method to exclude matching routes from the generated OpenAPI document.
#[HttpResponseStatus(...)]
Attach to a response Data class to change the generated HTTP status code for that response.
#[HttpResponseStatus(201)] class CreatedPostData extends Data { // ... }
#[CustomContentType(...)]
Attach to a Data class to emit request and response content under one or more custom content types.
#[CustomContentType(type: ['application/json', 'application/xml'])] class ExportData extends Data { // ... }
#[Tags(...)]
Attach to a controller class or method to add OpenAPI tags to the generated operation.
Configuration
The published config file lives at config/openapi-generator.php.
Important options:
openapi: OpenAPI version to generate. Default:3.0.2version: API version shown in the document. Default:1.0.0name: API title shown in the document. Default:OpenAPIpath: output path for the generated JSON fileincluded_route_prefixes: only routes with these URI prefixes are documentedignored_route_names: route names to excludeignored_methods: HTTP methods to skip, such asHEADandOPTIONSsecurity_middlewares: middleware-to-security-scheme mappingoverlay_files: optional OpenAPI JSON files to add manual documentation gapsnamespace_aliases: alias internal namespaces in generated schema nameserror_scheme_class:Dataclass used for generated error responses
Example namespace aliasing:
'namespace_aliases' => [ 'App\\Domain\\Internal\\Api' => 'Public\\Api', ],
This keeps schema names stable and avoids exposing internal namespace structure in your OpenAPI document.
Overlay spec files
Use overlay_files when you need manual additions that cannot be inferred from Laravel routes or Data classes, such as legacy endpoints, custom headers, or extra response variants.
'overlay_files' => [ resource_path('api/overlay.json'), ],
Overlay specs are additive. They can add:
- New
paths. - New methods under existing paths.
- New
responsesby status code under an existingpaths.{route}.{method}operation. - New
components.schemasentries. - New
components.securitySchemesentries.
The generated spec is authoritative. If the generated file already has a path, method, response status code, schema, or security scheme with the same key, that generated value is kept and the overlay value is ignored for that key. For example, if both generated output and the overlay define GET /api/users response 200, the generated 200 response wins; the overlay can still add 404 or other missing status codes.
Security integration
Routes using configured auth middleware are emitted with OpenAPI security requirements.
If a route includes authorization middleware such as can:update-posts, the generated operation description also includes the required permissions and the package adds 403 Forbidden responses alongside 401 Unauthorized where appropriate.
Notes and limitations
- Request and response inference is built around
Spatie\LaravelData\Data. - Array and
DataCollectionresponses should have precise return docblocks so item types can be resolved. GETendpoints do not emit a request body. If aDataobject is used there, its properties are converted into query parameters.
Optional frontend integration
If you use vite-plugin-pwa, exclude /api/ routes from the service worker:
VitePWA({ workbox: { navigateFallbackDenylist: [ new RegExp('/api/.+'), ], }, })
Minimal Vue page:
<route lang="json"> { "meta": { "public": true } } </route> <template> <iframe :src="url" style="width: calc(100vw - 40px); height: calc(100vh - 80px); border: none;" /> </template> <script lang="ts" setup> const url = `${import.meta.env.VITE_APP_URL}/api/openapi`; </script>
Development
make install
make test
You can also start the container with make dev and run commands from there.
For test coverage, run `make test-coverage``