Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Openapi Php Laravel Package

erasys/openapi-php

Erasys OpenAPI PHP lets you build OpenAPI 3 specifications directly in PHP using typed classes and fluent builders for schemas, paths, operations, parameters, and responses. Keep API documentation close to your code and easy to generate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require erasys/openapi-php

Requires PHP 8.x (the library leans on named arguments and constructor property promotion, so you get autocompletion for every OpenAPI field for free).

Where to look first

  1. src/Spec/V3/ — one class per OpenAPI 3.0 object (OpenApi, Info, Paths, PathItem, Operation, Response, Schema, MediaType, Components, Reference, …). The constructor argument names mirror the spec field names exactly, so the OpenAPI 3.0 spec page is your real documentation.
  2. tests/ — the fixtures show fully-built documents; the fastest way to learn the API is to copy a test case and mutate it.
  3. src/Validator/ — the rules engine you run to prove the document is legal.
  4. src/Serializer/ — turns the object tree into JSON for serving or committing.

First use case: a one-endpoint document, validated and serialized

use Erasys\OpenApi\Spec\V3\OpenApi;
use Erasys\OpenApi\Spec\V3\Info;
use Erasys\OpenApi\Spec\V3\Paths;
use Erasys\OpenApi\Spec\V3\PathItem;
use Erasys\OpenApi\Spec\V3\Operation;
use Erasys\OpenApi\Spec\V3\Response;
use Erasys\OpenApi\Validator\Validator;
use Erasys\OpenApi\Serializer\Serializer;

$openApi = new OpenApi(
    info: new Info(title: 'Orders API', version: '1.0.0'),
    paths: new Paths([
        '/orders/{id}' => new PathItem([
            'get' => new Operation(
                operationId: 'getOrder',
                responses: [
                    '200' => new Response(description: 'The order'),
                    '404' => new Response(description: 'Not found'),
                ],
            ),
        ]),
    ]),
);

// 1. Validate — never ship without this.
$result = (new Validator())->validate($openApi);
if (!$result->isValid()) {
    foreach ($result->getErrors() as $error) {
        fwrite(STDERR, $error->getMessage() . PHP_EOL);
    }
    exit(1);
}

// 2. Serialize for Swagger UI / Redoc / codegen.
file_put_contents(
    'public/openapi.json',
    (new Serializer())->serialize($openApi) // or json_encode($openApi, JSON_PRETTY_PRINT)
);

The payoff pattern: build → validate → serialize as a single artisan command or test, so an invalid spec breaks CI instead of breaking your docs site.

Implementation Patterns

Split the document into builder methods, not one giant constructor. A real spec is 500+ lines; keep it readable by returning PathItem/Operation objects from small functions or a dedicated OpenApiBuilder service, then assemble Paths from an array of them.

class SpecBuilder
{
    public function build(): OpenApi
    {
        return new OpenApi(
            info: $this->info(),
            servers: [new Server(url: 'https://api.example.com/v1')],
            paths: new Paths(array_merge(
                $this->orderPaths(),
                $this->customerPaths(),
            )),
            components: $this->components(),
        );
    }
}

Use Components + Reference for anything reused. Define each schema once in Components(schemas: [...]) and point at it with new Reference(ref: '#/components/schemas/Order'). This is the single biggest lever for keeping the builder maintainable, and it is also what makes generated clients usable — inline schemas produce InlineResponse2001-style class names.

components: new Components(
    schemas: [
        'Order' => new Schema(
            type: 'object',
            required: ['id', 'total'],
            properties: [
                'id'    => new Schema(type: 'integer', format: 'int64'),
                'total' => new Schema(type: 'number', format: 'float'),
                'items' => new Schema(
                    type: 'array',
                    items: new Reference(ref: '#/components/schemas/OrderItem'),
                ),
            ],
        ),
    ],
    responses: [
        'NotFound' => new Response(description: 'Resource not found'),
    ],
)

Validate in a PHPUnit test, not just in a command. A test like test_spec_is_valid() that asserts isValid() (and asserts zero errors rather than "no fatal errors") catches regressions the moment someone adds a parameter with a typo'd in: value.

Diff the serialized output in CI. Commit openapi.json and let the test fail when the generated JSON changes without the file being regenerated. Reviewers then see spec changes as a readable git diff instead of a PHP diff — invaluable when the API contract is consumed by other teams.

Generate from your routes, then hand-polish. The common production workflow is a php artisan openapi:generate command that iterates Route::getRoutes(), infers path parameters from the URI ({id} → Parameter(name: 'id', in: 'path', required: true, schema: new Schema(type: 'string'))), and produces a skeleton. Response schemas are then filled in by hand, because routes never carry that information. Keep the generator and the hand-written Components in separate classes so regeneration never clobbers the curated part.

Model request bodies as a RequestBody with a MediaType per content type, and always set required: true explicitly — omitting it defaults to false in the spec, which surprises people who assume POST bodies are mandatory.

Serve the artifact, not the library. In production, the docs route should return the pre-generated openapi.json file (or a cached Serializer output). Do not build the spec object graph on every request — it is pure overhead and adds a failure mode to a route that should never 500.

Vendor extensions are just extra constructor arguments. Anything starting with x- can be passed alongside the standard fields, which is handy for tagging operations with internal metadata (owner team, auth scope) that your tooling reads but Swagger UI ignores.

Gotchas and Tips

OpenAPI 3.0 only — not 3.1. There is no jsonSchemaDialect, no type: ['string', 'null'], no webhooks top-level key. Stay on 3.0 syntax: nullable: true on a Schema, not a union type. If your consumers need 3.1, this package is the wrong tool.

Validation is opt-in and advisory. Constructing an OpenApi object never throws for a malformed document — you can happily build new Response() with no description and serialize it. Nothing stops you from publishing an invalid spec unless you explicitly call the validator and act on the result. Wire it into CI; do not trust that "it constructed, so it's fine."

Report all errors, not the first. The result object collects multiple violations. Print the full list with their paths — a single "invalid document" message will cost you an afternoon hunting through nested Paths arrays. If a Reference points at a component you forgot to declare, the validator is the only thing that will tell you.

Named arguments are part of the public API. Because nearly every call site is new Schema(type: 'object', ...), a rename upstream in a minor release silently breaks your build (or worse, PHP 8 will happily accept a differently-named argument that now means something else). Pin the version in composer.json and read the changelog before bumping. Last release was 2023-11-06, so treat the package as stable-but-frozen and budget for the occasional compatibility patch yourself rather than expecting upstream fixes for PHP 8.4 deprecations.

Empty collections vs. empty objects in JSON. new Paths([]) must serialize to {"paths": {}}, not "paths": [] — the spec requires an object. If you hand-roll serialization with json_encode, verify the empty-collection cases explicitly; it is the classic way to produce a document that Swagger UI refuses to load with a cryptic parse error. Prefer the package Serializer over raw json_encode for exactly this reason.

Numeric-looking keys and PHP array coercion. responses: ['200' => …] becomes integer key 200 in PHP. JSON output is usually still correct (non-sequential integer keys serialize as an object), but if you ever build a map with keys 0, 1, 2 — for example response codes in a test fixture — you will get a JSON array where an object was required. Keep status codes as the literal spec strings and don't reindex those arrays.

Reference objects are opaque to validation. The validator checks that the $ref string is well-formed; it does not follow it into Components and verify the target exists with the right shape. Validate your component definitions as if they were standalone, and keep ref strings as constants shared between the Reference and the Components map so a typo can't hide in two places.

Don't nest object graphs where a $ref belongs. Recursive models (an Order containing Order[]) can only be expressed through references. Trying to inline the recursion will blow the stack while building or serializing. The rule of thumb: if a schema can point back to itself, it lives in Components and is used exclusively via Reference.

Extension points to lean on: subclass the Spec\V3 classes to attach your own helpers (e.g. a JsonSchema that auto-fills format from a PHP type), and wrap the Serializer if you need YAML or post-processing like injecting a server URL per environment. Both are plain, non-final objects, so this is cheap — and given the project's size and low release cadence, local extension is a more realistic strategy than waiting for upstream features.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings