phpro/api-problem
A framework-agnostic RFC 7807 Problem Details implementation for PHP, providing an interface, an ApiProblemException, and built-in HTTP problem types (400–428) for consistent API error responses, with Symfony integration available.
Architecture fit
phpro/api-problem is a framework-agnostic value-object library, not a middleware or bundle. It defines ApiProblemInterface::toArray(), an ApiProblemException carrier, and ~13 prebuilt HTTP problem classes that auto-populate type/title from the status code. The README is explicit that "handling the exceptions is up to the framework," and the only listed integration is a Symfony bundle. In a Laravel codebase this means the package gives us a standardized error payload shape and a small type hierarchy, and nothing else — no service provider, no auto-discovery, no renderable, no application/problem+json content negotiation. That is actually a reasonable division of labor, but a TPM must budget for the glue code as first-party scope, not assume it ships with the dependency.
Integration feasibility
Feasible and small. The realistic Laravel implementation is roughly 80–150 lines in one place: register renderables for ApiProblemException (and optionally map framework exceptions) in bootstrap/app.php (->withExceptions(...)) for Laravel 11+, or app/Exceptions/Handler.php for 10 and below; serialize toArray() via response()->json($payload, $status, ['Content-Type' => 'application/problem+json']). Custom problems are trivial to add by extending HttpApiProblem or implementing the interface.
Two concrete friction points:
ValidationApiProblem requires symfony/validator (a ConstraintViolationList), so it cannot consume Laravel's ValidationException directly. We either pull a Symfony component into a Laravel app purely as a DTO adapter or hand-roll a problem class for Laravel's validator output.Technical risk
2026-09-14 release date is anomalous and should be verified against Packagist before it appears in any dependency decision record.type/title guidance and media-type registration changed; committing to 7807 language in our public API contract now creates a small future docs/contract debt.ExceptionApiProblem embeds message, file, line, and full stack trace. Must be strictly gated on app.debug and excluded from any externally reachable environment.veewee) plus drive-by contributors. MIT license and a tiny surface mean forking is cheap — mitigate by vendoring-friendly design.Key questions
application/problem+json, type as resolvable URI)? Decide once, document in the OpenAPI spec.Stack fit
Good conceptual fit with Laravel's exception pipeline and renderable/reportable hooks, and with Laravel's existing JSON error output. It fits worst where Laravel has strong opinions: validation errors ({message, errors{}}), authentication redirects, and the default Handler shape. The package is not a drop-in replacement for Handler; it is a payload vocabulary consumed by it.
Migration path
App\Support\Http\ProblemFactory + renderable behind a config flag (api.problem_details.enabled), defaulting off in production.ModelNotFoundException/NotFoundHttpException → NotFoundProblem, AuthenticationException → UnauthorizedProblem, AuthorizationException → ForbiddenProblem, ValidationException → custom validation problem (or ValidationApiProblem if we accept the Symfony dependency), ThrottleRequestsException → custom 429, ApiProblemException → passthrough.Problem component) and generate a contract test suite (JSON Schema assertions) that runs in CI against every error path.message/errors.Compatibility
Requires PHP 8.x per recent releases — fine for Laravel 10/11/12 stacks. No Laravel version constraint exists because there is no Laravel coupling, so no framework-version lock risk; conversely, no upstream guarantee that our Laravel glue keeps working across majors — we own that. symfony/validator is an optional dependency only if we use ValidationApiProblem; avoiding it keeps the dependency tree clean.
Sequencing
Contract decision (7807 vs 9457, field set incl. instance, traceId) → adapter + renderable → contract tests + OpenAPI → staging → external comms/versioning → production default. Estimate: 1 sprint for internal-only; 1–2 quarters if the envelope is a breaking public contract.
Maintenance Very low code churn (small, stable surface, MIT, no transitive deps unless we opt into Symfony Validator). The real recurring cost is the Laravel adapter we write and the OpenAPI/contract test we maintain. Pin with a caret constraint and review at each PHP/Laravel major; a stale upstream is survivable because forking ~1,000 LOC of value objects is genuinely cheap.
Support
No vendor support; GitHub issues only. Support burden lands on our platform team for both the package and the adapter. Developer ergonomics are strong (one-line throw), which reduces "how do I return an error?" churn in code review — a real, if soft, ROI.
Scaling Effectively zero runtime cost: pure value objects, no I/O, no caches, no bootstrapping. Negligible per-request allocation on error paths only. No scaling concern at any traffic level.
Failure modes to instrument
Content-Type: application/problem+json (client parsers silently fall back).trace/file/line into non-debug environments — enforce in config and add a test asserting the key is absent when app.debug=false.abort() paths, queue/console contexts) returning the legacy shape, producing a two-format API.errors{} and RFC violations[], breaking existing client SDKs.title (human-readable, may change) rather than type (stable URI) — document that type is the contract.Ramp-up
Hours, not weeks: a mid-level Laravel developer can adopt the pattern in a single PR; the learning curve is the RFC vocabulary (type vs title vs detail, instance, about:blank), not the library. Budget one short internal doc/written standard so teams stop hand-rolling ad-hoc error arrays — that consistency is the primary product outcome this package buys us.
How can I help you explore Laravel packages today?