Spec-first, not code-first
The OpenAPI document drives the code, not the other way around. This is the opposite of annotation tools like l5-swagger or scramble, where your PHP generates the spec.

A single object schema in your spec becomes a laravel-data class with an explicit rules() method, plus a native backed enum for any enum it references.
// generated from components.schemas.Customerfinal class CustomerData extends Data{ public function __construct( public readonly int $id, public readonly string $name, #[MapName('email_address')] public readonly ?string $emailAddress = null, public readonly ?CustomerStatus $status = null, ) {}
public static function rules(): array { return [ 'id' => ['required', 'integer', 'min:1'], 'name' => ['required', 'string', 'max:255'], 'email_address' => ['sometimes', 'nullable', 'string', 'email'], 'status' => ['sometimes', Rule::enum(CustomerStatus::class)], ]; }}// generated from components.schemas.Customer.properties.statusenum CustomerStatus: string{ case Active = 'active'; case Inactive = 'inactive'; case Pending = 'pending';}components: schemas: Customer: type: object required: [id, name] properties: id: type: integer minimum: 1 name: type: string maxLength: 255 email_address: type: string format: email nullable: true status: $ref: '#/components/schemas/CustomerStatus' CustomerStatus: type: string enum: [active, inactive, pending]openapi.yaml └── openapi-laravel → app/Data/CustomerData.php (laravel-data class + rules()) app/Data/CustomerStatus.php (native backed enum) app/Data/CustomerWritableData.php (write variant, when spec uses readOnly/writeOnly) app/Http/Controllers/Api/AbstractPetController.php (abstract controller per tag) routes/api.generated.php (generated routing table)You write your business logic. The DTOs, validation, controller signatures, and routing table stay in sync when the spec changes. Re-run the generator and the diff tells you exactly what moved.
The whole loop is proven end to end: one petstore spec drives a generated Laravel backend and a TypeScript SPA built with openapi-zod-ts, exercised by a Playwright suite over real HTTP. See the end-to-end demo guide.

Spec-first, not code-first
The OpenAPI document drives the code, not the other way around. This is the opposite of annotation tools like l5-swagger or scramble, where your PHP generates the spec.
Code you own
Output is readable PHP in your repo. Review it, commit it, read it in a diff. No opaque runtime and no reflection magic you cannot follow.
Explicit validation
Rules are emitted verbatim from spec constraints (required, nullable, maxLength, pattern, format, minimum, maximum, enum), not guessed from property types at runtime.
Server scaffold, out of the box
The default run already emits abstract controllers per tag and a routes file, with typed and
validated query, path, and header parameters and typed request and response bodies. The routing
table derives from the spec, so path-level drift is structurally impossible. Opt out with
--no-controllers / --no-routes.
Drift gate for CI
openapi:check recomputes the full file set in memory and compares it byte-for-byte against
disk: exit 1 fails the build the moment committed code and spec disagree. Generate and check
share one planner, so they are always in lockstep.
Validation that is differentially tested, not assumed
A differential oracle runs spec-valid and spec-invalid payloads through the generated rules() with the real Laravel Validator: what the spec rejects, the generated rules() must reject. The test corpus is 135 published real-world OpenAPI documents that must parse, generate, and compile on every CI run. See Philosophy: quality is the feature.
Deterministic
Stable ordering everywhere. The same spec in produces byte-identical files out, so the generator is safe to run in CI and commit.
Runs without Laravel
The same generator ships as a framework-free binary, so non-Laravel projects and CI can run it too.
Install the package.
composer require --dev codewithagents/openapi-laravelPoint it at your spec and generate. One command, full output.
php artisan openapi:generate --spec=openapi.yaml --output=app/DataOut of the box this emits the Data classes with spec-derived rules(), the native enums, one
abstract controller per tag, and a routes file, all typed against each other. Models only?
Pass --no-controllers --no-routes.
Implement the controllers and keep it in sync.
Extend each abstract controller with your business logic; an unimplemented operation is a PHP fatal, not silent drift. Add the drift gate to CI so the committed code can never quietly diverge from the spec:
php artisan openapi:check # exit 0 in sync, 1 drift, 2 config errorQuick start
From a spec file to generated Data classes in a few minutes. Read the quick start
GitHub
Source code, issues, and the 135-spec corpus. View on GitHub