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

Laravel Api Problem Laravel Package

api-skeletons/laravel-api-problem

Laravel API Problem provides RFC 7807-compliant problem details responses for JSON APIs, helping you return consistent, machine-readable errors with exception and validation support. It integrates with Laravel to standardize API error payloads and status codes.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require api-skeletons/laravel-api-problem. Laravel package auto-discovery registers the service provider; no manual providers entry needed on 9/10/11/12.
  • Publish the config so you can see the knobs before you need them: php artisan vendor:publish --tag=api-problem → config/api-problem.php. Read this file first — it tells you which defaults (title/status/about:blank handling) the package will apply when you throw a bare exception.
  • First use case: take one existing API endpoint that currently returns Laravel's default {"message": "..."} error and make it return an RFC 7807 application/problem+json body instead. Register the package middleware on your API group (Laravel 11/12: bootstrap/app.php ->withMiddleware(...); Laravel 10 and below: $middlewareGroups['api'] in app/Http/Kernel.php), then throw the package's ApiProblemException from a controller with status, title, detail, and instance.
  • Verify the contract immediately rather than trusting it: curl -i the endpoint and confirm both the Content-Type: application/problem+json header and the five standard members (type, title, status, detail, instance).
  • Confirm what you actually installed after upgrading: composer show api-skeletons/laravel-api-problem (expect 2.0.1) and composer why instantiator to see who is pulling the newly-bumped dependency.
  • Source worth skimming: the response class (how members are serialized), the middleware (where it sits in the stack), and the exception class (constructor signature — this is what you'll type most often).

Implementation Patterns

  • Throw from the domain, render at the edge. Keep ApiProblemException out of your models/services; let controllers or a small Exception → Problem mapper decide status/title/detail. That keeps HTTP semantics out of business logic and makes the problem format swappable.
  • Two integration points, pick one per app. Either (a) let the middleware act as a catch-all so every exception on API routes becomes a problem document, or (b) wire explicit ->render() callbacks in withExceptions() for the handful of exception types you care about. Mixing both leads to double-wrapping and confusion about which layer produced the body.
  • Validation errors are the highest-value mapping. Catch ValidationException and emit 422 with an errors extension member (RFC 7807 permits extension members alongside the standard ones). Consumers then have one error parser for the whole API instead of two shapes.
  • Use type as your stable, documented error code. A URI like https://api.example.com/problems/insufficient-funds gives clients something to branch on that survives copy changes to title and detail. Keep a table of them in your OpenAPI spec and treat changes as breaking.
  • Set instance to the request URI (or a request/correlation id). It's the cheapest debugging win in the whole package — a support ticket with a problem body becomes traceable to a log line.
  • Scope it to /api/*. Apply the middleware to the API group only; web routes should keep Laravel's HTML error pages and session-based redirects. Blanket registration produces problem+json for browser hits, which nobody expects.
  • Contract-test in feature tests. $response->assertStatus(422)->assertHeader('Content-Type', 'application/problem+json')->assertJsonPath('type', '...'). Assert the header explicitly — the body shape is only half the contract.
  • Document once, $ref everywhere. Define a single Problem schema in your OpenAPI doc and reference it from every 4xx/5xx response. It keeps generated clients honest.
  • Upgrade workflow for this release (2.0.1): composer update --with-all-dependencies api-skeletons/laravel-api-problem, run the suite, then re-run your API contract tests. The release is a dependency move (instantiator 2.0), not an API change, so tests passing means you're done — no code migration expected.

Gotchas and Tips

  • Accept header negotiation bites. If a client sends Accept: */* or no Accept at all, Laravel's expectsJson() may return false and you'll get HTML back from paths you thought were JSON. Force the behaviour on API routes rather than relying on negotiation, and make sure your docs tell clients to send Accept: application/json.
  • 404/405 on unknown routes is the classic hole. Route-not-found is raised before your route middleware runs in some configurations, so the middleware catch-all never sees it. Explicitly test GET /api/does-not-exist — if you get HTML, handle NotFoundHttpException/MethodNotAllowedHttpException in the exception handler too.
  • status in the body must equal the HTTP status. The single most common bug in hand-rolled implementations is a 422 header with a 500 in the body (or vice versa). Never set them in two places — derive one from the other.
  • type must be an absolute URI per the RFC. Relative values pass in many clients and break strict ones; about:blank is the correct choice when the title is just the generic status phrase.
  • Don't leak internals. In production, keep detail user-facing and generic, and put the real exception in report()/logs. Never map $e->getMessage() from a QueryException straight into detail.
  • Extension member names must not collide with type, title, status, detail, or instance. Pick a namespace (e.g. errors, trace_id) and stick to it.
  • CORS: application/problem+json isn't in default allowed content types for some CORS setups — browsers then can't read the error body even though the request failed correctly. Add it to your CORS config.
  • Log hygiene: 4xx problem responses are not application errors. Make sure your middleware/handler calls report() selectively (5xx only) or you'll drown the log.
  • Composer resolution on 2.0.1: the instantiator 2.0 bump is a major-version move on a dependency. If your app or another package pins instantiator ^1.x, composer update will fail with a conflict. Resolve with composer update --with-all-dependencies or align the other constraint — do not downgrade this package to force a resolution. If the new instantiator requires PHP 8.1, CI images still on 8.0 will fail at composer install; check your matrix before merging.
  • Extension points: the published config, the response class (subclass it to add default members), the middleware (override or wrap to add correlation ids), and the exception-to-problem mapping are all fair game. Prefer subclassing/wrapping over editing vendor/, and re-run vendor:publish --force consciously — it will overwrite your config edits.
  • Pin it. Because the wire format is your public contract, keep an exact constraint in composer.json for API-critical services and let the lockfile + contract tests, not ^ ranges, approve upgrades.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource