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

Api Validator Laravel Package

eleven-labs/api-validator

PHP library for validating PSR-7 HTTP requests and responses against OpenAPI/Swagger 2.0 schemas. It checks headers, query parameters, URI, and body using JSON Schema, helping ensure API messages conform to your specification.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package. It's a framework-agnostic library that validates PSR-7 requests and responses against a Swagger 2.0 description. There is no service provider, no config file, no auto-discovery, and at 24 stars / 0 dependents you should assume you'll be reading vendor/eleven-labs/api-validator/src rather than Googling for answers.

Minimal path to first use:

  1. Put a valid Swagger 2.0 file in the repo — docs/api/swagger.json. Validate it in the Swagger Editor before you do anything else. If you only have an OpenAPI 3 spec, stop here; this package cannot read it.
  2. Install it plus a PSR-7 bridge, since Laravel speaks Illuminate\Http\Request:
    composer require eleven-labs/api-validator nyholm/psr7 symfony/psr-http-message-bridge
    
  3. Bind the parsed schema once. Parsing is the expensive part, so make it a singleton:
    // app/Providers/ApiContractServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(\ElevenLabs\Api\Schema::class, fn () => new \ElevenLabs\Api\Schema(
            base_path('docs/api/swagger.json')
        ));
    }
    
  4. First use case: assert against the spec in your test suite, not in production traffic. Given the maturity, the highest-value, lowest-risk entry point is a helper that validates the PSR-7 response of your feature tests. That immediately catches drift between controllers/resources and the documented contract, with zero runtime blast radius:
    $response = $this->getJson('/api/pets/42');
    
    $psrResponse = app(PsrHttpFactory::class)->createResponse($response);
    (new \ElevenLabs\Api\Validator\ResponseValidator($schema))->validate($psrResponse);
    

Where to look first in vendor/: the Schema, Definition and Validator namespaces, then the package's own tests/ directory — for a readme-maturity project, the tests are the documentation. Confirm the actual constructor signatures there before copying any snippet (including these).

Implementation Patterns

Test-time contract assertions (adopt this first). Wrap the validator in a PHPUnit/Pest assertion and use it on your JSON API tests. Response validation is where this library earns its keep: your request shapes are already covered by FormRequests, but nothing stops a controller from returning a field the spec doesn't declare. Run it as a separate contract test suite in CI so it doesn't slow the main suite.

Boundary-guard middleware (adopt second, behind a flag). Convert once, validate, then hand the original Laravel request to the controller:

final class ValidateAgainstSpec
{
    public function __construct(
        private \ElevenLabs\Api\Schema $schema,
        private PsrHttpFactory $psrFactory,
    ) {}

    public function handle(Request $request, Closure $next)
    {
        $psrRequest = $this->psrFactory->createRequest($request);

        try {
            $definition = $this->schema->getRequestDefinition(
                $psrRequest->getMethod(),
                $psrRequest->getUri()->getPath()
            );
            (new RequestValidator($definition))->validate($psrRequest);
        } catch (DefinitionNotFoundException) {
            // Undocumented route — log and continue, or 501 if you enforce spec-first.
        } catch (ConstraintViolationException $e) {
            throw new HttpResponseException(response()->json([
                'title'  => 'Request does not match the API contract',
                'status' => 400,
                'violations' => $e->getViolations(),
            ], 400, ['Content-Type' => 'application/problem+json']));
        }

        return $next($request);
    }
}

Register it as api.spec in bootstrap/app.php / Kernel and attach it only to routes you've fully described. Gate it behind config('api.contract.enforce') so you can turn it off per environment.

Schema-as-singleton for Octane and queue workers. The schema is immutable once built, so a singleton survives between requests under Octane and pays the parse cost once. If parse time still hurts, pre-convert YAML to JSON and json_decode into an array at deploy time, then feed the array to Schema if the constructor allows it.

Contract-first workflow. Treat swagger.json as the source of truth: a CI job runs Spectral/swagger-cli on the spec, a second job runs the contract test suite, and PRs that change behaviour must change the spec. This is the pattern that makes a 24-star dependency worth the risk — you're buying spec enforcement, not a feature.

Per-version schemas. For /api/v1 and /api/v2, register two Schema instances in a small registry keyed by version and resolve in the middleware from the route prefix. Don't try to cram versions into one spec with basePath switching.

Abstract it. Put your own ApiContractValidator interface in front of the library and make the middleware depend on that. Swapping in league/openapi-psr7-validator (OpenAPI 3 capable) later becomes a one-file change.

Gotchas and Tips

  • Swagger 2.0 only. No OpenAPI 3, no RAML, no API Blueprint despite the README's roadmap. Converting 3.x down with api-spec-converter is lossy (nullable, oneOf, anyOf, requestBody all get mangled). If your spec is 3.x, this package is the wrong choice.
  • JSON, not YAML. The underlying justinrainbow/json-schema parses JSON. Author in YAML if you like, but emit swagger.json as a build artefact and consume that.
  • PSR-7 conversion is the sharp edge. The body is a stream: $psrRequest->getBody() is consumed after validation, so call rewind() if you reuse it. withParsedBody is not populated by the bridge, so anything the validator expects in the parsed body must come from decoding the raw stream. Uploaded files and multipart/form-data bodies are effectively out of scope — exclude those routes.
  • Path resolution fails quietly. Undocumented routes, basePath mismatches, trailing slashes, and proxies rewriting the URI all produce a "definition not found" rather than a validation error. Distinguish that from a genuine violation in your error mapping, otherwise misconfigured specs look like client errors.
  • additionalProperties: false is the most common self-inflicted outage. If your spec is strict and a client sends an extra key, the request is rejected in production. Audit before enforcing.
  • format: date-time, uuid, email are strict. PHP's DateTime string output can fail date-time if the spec/validator pair doesn't tolerate offsets. Test fixtures early.
  • Draft support is whatever justinrainbow/json-schema you resolve. Swagger 2.0 is draft-04; don't expect draft-07 keywords to behave.
  • No error messages for humans. Violations are machine-shaped; map them to Problem+JSON and log the request ID. Never echo the raw violation payload in production.
  • Debugging a false rejection: dump the resolved RequestDefinition and json_encode() the generated schema, then diff it against your spec. Nine times out of ten it's $ref resolution, basePath, or a content-type mismatch.
  • Don't validate every request on hot endpoints without measuring. Even with a cached schema, JSON Schema validation is not free, and response validation in production doubles the work. Response validation belongs in tests; request validation belongs at the boundary, ideally only on mutating routes.
  • Pin the version exactly and be prepared to fork. With zero dependents and readme-level maturity, upstream is unlikely to fix your edge case. Keep the dependency behind your own interface, keep a fork path open, and don't let it leak into application code.
  • Validation is not authorization. Spec conformance says nothing about whether this user may touch this pet. Keep your policies.
  • 204/HEAD responses have empty bodies and often trip response validators; skip them explicitly.
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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style