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.
HttpApiProblem (auto-fills type/title from status code), an optional DebuggableApiProblemInterface, and ~12 pre-built status types. The realistic alternatives are (a) ~150 lines of in-house value objects plus your own taxonomy, or (b) a heavier API-platform bundle. This package wins on scope discipline — but you still own the framework glue (exception renderer), so the "buy" only covers the vocabulary, not the plumbing.type URI is a stable, machine-readable error identifier. Committing to that shape enables client-side branching on error type, generated API docs, and error-class analytics. It also creates a compatibility obligation: once type strings are published, changing them is a breaking change. Decide this deliberately, before GA.ExceptionApiProblem emits message, type, code, file, line, trace, and chained previous exceptions — but only via toDebuggableArray(). That maps directly onto a release-engineering decision: verbose exception payloads in staging/local, bare payloads in production. PMs should own the "never leak traces to untrusted clients" gate and confirm the environment switch is wired.ValidationApiProblem (Symfony Validator ConstraintViolationList, with per-violation propertyPath, title, and UUID type) is the shape your front-end maps to form fields. Choosing it decides how much client-side error-mapping work your web app or partner integrators must do.ApiProblemBundle, Symfony ^4.1). There is no service provider, no publishable config, no Laravel exception-handler recipe, no HTTP assertion helpers. That is either a half-day of internal glue you budget for, or a candidate for a small internal package/fork — and it is the main reason to treat this as a component, not a solution.type values point at w3.org/Protocols/rfc2616/... links, which are dated. If you publish type URIs to external consumers, plan to own your own documented type registry rather than inheriting these.Adopt it when:
renderable/toResponse hook in the exception handler; the package gives you the payload, you give it the hook.ApiProblemException + DebuggableApiProblemInterface) rather than hand-rolling it.ApiProblemException without importing HTTP framework types.Look elsewhere, or budget extra, when:
spatie/laravel-ignition-style error packages, treblle/laravel-api-response style envelopes) or API-platform frameworks that generate error schemas from your OpenAPI spec.title/detail.ApiProblemInterface-shaped seam so swapping is a one-file change.ValidationException payload and front-ends depend on it. Migrating to ValidationApiProblem is a client-visible breaking change; treat it as a versioned API change with a deprecation window, not a refactor.Evaluation checklist before you commit: confirm the PHP constraint matches your runtime (releases track 8.2/8.3/8.4/8.5 support); read the CHANGELOG for breaking changes in payload shape; verify the debug switch is environment-driven; decide and document your type URI strategy up front.
For executives (one paragraph): Every error our API returns is currently a slightly different shape, which means every partner integration and every client app re-learns our error handling, and our support team fields avoidable tickets. This is a small, MIT-licensed, dependency-free PHP library that implements the industry-standard Problem Details format (RFC 7807, IETF) so all our API errors come back in one predictable, machine-readable structure — including a safe, toggleable "verbose" mode for staging that keeps stack traces and file paths out of production responses. Cost is near zero: a Composer dependency plus roughly half a day of engineering to wire one response hook. Risk is contained and reversible: we isolate it behind an internal interface, so replacing it later is a single-file change. Expected benefit is faster partner onboarding, fewer "what does this error mean" escalations, and a stable error contract we can version deliberately instead of discovering breakage in production. The one caveat I'd flag: the library is niche (small maintainer base, no Laravel integration shipped), so we should not present it as a vendor commitment — we're adopting a format, and pinning a small piece of code that implements it.
For engineering (talk track): composer require phpro/api-problem, throw ApiProblemException with an HttpApiProblem (or one of the 12 built-in problems: 400/401/403/404/405/409/412/415/418/422/423/428), and render it from one place in the exception handler. The domain layer stays framework-agnostic — the package is an interface plus value objects, not middleware. Custom problems: extend HttpApiProblem and pass extra members; implement DebuggableApiProblemInterface if you need debug-only fields. Two things we own: (1) the Laravel glue — there is no service provider, so we write and test the renderer ourselves; (2) the type URI registry — the defaults point at legacy w3.org RFC 2616 anchors and RFC 7807 has since been superseded by RFC 9457, so if we publish type values externally we should point them at our own documented error pages. Guardrails: pin the version, keep the seam behind our own interface, add contract tests asserting the JSON shape on a representative error from each status class, and assert in a test that production responses never include the exception block.
How can I help you explore Laravel packages today?