Skip to content

Quick start

This guide takes you from a spec file to a working, typed Laravel slice: models, validation, controllers, routes, and a CI gate that keeps them in sync. There are two ways to run the generator: the artisan command inside a Laravel app, and the standalone binary for anything else.

  1. Install the runtime peer and the generator.

    The generated classes extend Spatie\LaravelData\Data, so spatie/laravel-data is a runtime dependency of your app. Without it, a fresh app hits fatal autoload errors as soon as it loads a generated class. The generator itself is a dev dependency.

    Terminal window
    composer require spatie/laravel-data:^4.23
    composer require --dev codewithagents/openapi-laravel
  2. Generate from your spec. One command, full output.

    Terminal window
    php artisan openapi:generate --spec=openapi.yaml --output=app/Data

    For the classic Petstore spec, this is what appears on disk:

    app/Data/Pet/PetData.php laravel-data class + spec-derived rules()
    app/Data/Pet/CategoryData.php per-tag directories mirror the controllers
    app/Data/Pet/TagData.php
    app/Data/Pet/ApiResponseData.php
    app/Data/Store/OrderData.php
    app/Data/User/UserData.php
    app/Http/Controllers/Api/AbstractPetController.php one abstract controller per tag
    app/Http/Controllers/Api/AbstractStoreController.php
    app/Http/Controllers/Api/AbstractUserController.php
    routes/api.generated.php one Route:: entry per operation

    The output namespace comes from config/openapi-laravel.php (output.namespace, default App\Data); set it in the config if you need a different namespace, or override it per run with --namespace. Set spec and output.path there too and you can then just run:

    Terminal window
    php artisan openapi:generate

    Want models only? Pass --no-controllers --no-routes (see the precedence rules).

  3. Scaffold the concrete controllers, then wire them up.

    The generated routes file references concrete controllers (PetController) that extend the generated abstracts. Scaffold them in one command so the app boots immediately:

    Terminal window
    php artisan openapi:scaffold

    This writes one stub per controller, each method throwing a LogicException placeholder. The stubs are yours from that moment: re-running the command (or the generator) never overwrites them, and the drift gate ignores them. Replace each placeholder with your implementation:

    final class PetController extends AbstractPetController
    {
    public function show(int $petId): PetData
    {
    return PetData::from(Pet::findOrFail($petId));
    }
    // ... the abstract class forces you to implement the rest
    }

    Then include the generated routes file from routes/api.php:

    require base_path('routes/api.generated.php');

    See the server scaffold guide for the full walkthrough.

  4. Review, commit, and add the drift gate to CI.

    The output is deterministic, so commit it like any other code. Then make CI fail whenever the committed files and the spec disagree:

    Terminal window
    php artisan openapi:check # exit 0 in sync, 1 drift, 2 config error

    See the drift-check guide for a ready-made GitHub Actions job.

Not a Laravel project, or running in a CI step that has no app bootstrapped? The same generator ships as a framework-free binary:

Terminal window
vendor/bin/openapi-laravel --spec=openapi.yaml --output=src/Data --namespace="Acme\\Dto"
FlagDescription
--specPath to your OpenAPI 3.x document (YAML or JSON)
--outputDirectory the generated files are written to
--namespacePHP namespace for the generated classes
--configPath to a JSON config file (default: ./openapi-laravel.json)

Controllers and routes are generated by default into <output>/Controllers and <output>/routes.php; override the locations with --controller-output / --routes-output, or skip them with --no-controllers / --no-routes. The one-time concrete controller stubs are available here too:

Terminal window
vendor/bin/openapi-laravel scaffold --spec=openapi.yaml --output=src/Data

For a Customer schema that references a CustomerStatus enum, the generator writes:

app/Data/CustomerData.php laravel-data class with a spec-derived rules() method
app/Data/CustomerStatus.php native backed enum

If the spec marks fields with readOnly or writeOnly, you also get a write variant such as CustomerWritableData.php. See generated output for full examples.

The controllers and routes that come with it

Section titled “The controllers and routes that come with it”

The default run already scaffolds one Abstract{Tag}Controller per OpenAPI tag and a routes/api.generated.php file, typed by the same Data classes. You extend each abstract controller with your own concrete implementation; php artisan openapi:scaffold writes the initial concrete stubs for you, one time, and regeneration only ever rewrites the Abstract* files. See the server scaffold guide for the full walkthrough, or disable the scaffold with --no-controllers --no-routes (or controllers.enabled / routes.enabled in the config).

Configuration

Tune the spec path, output, suffix, pruning, and depth. Configuration

Generated output

See a full Data class, a backed enum, and the read/write split. Generated output

Server scaffold

Generate abstract controllers and a routes file. Server scaffold