crell/api-problem
A framework-agnostic PHP library implementing RFC 9457 (formerly RFC 7807) Problem Details for HTTP APIs. It provides structured problem objects, exceptions, and response helpers for consistent, machine-readable API error responses.
Architecture fit
crell/api-problem is a focused, framework-agnostic implementation of RFC 9457 (formerly 7807) Problem Details for HTTP APIs. It ships immutable value objects (Problem, ApiProblem, ProblemException) and PSR-7/PSR-15 integrations. That is a deliberately narrow surface area — which is good for a Laravel codebase only if we treat it as a serialization contract, not as an HTTP layer.
The core fit question is not "does it work with Laravel" (it does, nominally) but "where do we anchor it." Two viable anchors:
Problem/ApiProblem as the canonical error DTO inside our own exception renderer. No PSR-7, no PSR-15. This is the low-risk, high-value path.psr/http-message, psr/http-server-handler, and a Symfony PSR-7 bridge to run Crell's middleware inside Laravel's pipeline. This adds a parallel HTTP abstraction that fights Laravel's Request/Response lifecycle and Octane's request handling.For a Laravel stack, option 1 is the only defensible default. The package's ProblemException does not extend Illuminate's exception contract, so Laravel's handler will not render it without an explicit mapping. That mapping is small, but it must be owned.
Integration feasibility
Straightforward, with caveats:
illuminate/*; the package is additive.renderable exception (or a render() method on a base exception) that returns an Illuminate\Http\Response with Content-Type: application/problem+json.Accept header check) is our responsibility in Laravel — the package will not inspect Accept for us outside its PSR-15 middleware.Technical risk
type URI and human-readable detail to clients. Without discipline, internal exception messages (SQL errors, stack traces, tenant IDs) leak into detail. This is a governance risk, not a library risk.Key questions
{ "error": { "code": ..., "message": ... } }) that clients already parse?crell/api-problem align?type URI registry (documentation site, versioning, deprecation)?Stack fit
Problem/ApiProblem to guarantee spec-correct output.Migration path
Phased, opt-in, reversible:
Problem serialization, and emit application/problem+json from one non-critical endpoint. Validate with existing API contract tests and a consumer smoke test.App\Exceptions\ApiProblemException extending RuntimeException and implementing Illuminate\Contracts\Support\Responsable, rendering via the package. New endpoints use it; existing endpoints are untouched.response()->problem(...) macro wrapping the value object, so controllers can return problems without throwing where a throw is inappropriate (e.g., validation warnings, partial results).App\Exceptions\Handler to map ValidationException, AuthenticationException, ModelNotFoundException, and HttpException into Problem Details. This is the moment where the API's error contract actually changes — gate it behind an API version bump.Compatibility
Crell\ApiProblem vs later ApiProblem\). Verify the exact namespace at install time.type URI resolution, instance field population, nested title/detail separation, and 4xx vs 5xx masking.Sequencing
Spike → base exception → response macro → global handler mapping → type registry documentation → consumer contract tests → API version gate → rollout.
Each step is independently valuable and independently revertible. That is the property that makes this package attractive despite its low score.
Maintenance
app/Support/ProblemDetails is a realistic fallback if upstream stalls. Document this contingency; it lowers the perceived risk from 249-star dependency to "self-owned code."Support
type URIs, and a policy for what goes in detail versus what gets logged only.Scaling
type URIs grows, so does the need for a registry and documentation site. That is a product/docs cost, not a runtime cost.Failure modes
detail reaching clients. Mitigation: a renderer that masks detail for 5xx and for any exception not explicitly allowlisted.application/problem+json; some CDNs and legacy clients only accept application/json. Mitigation: verify with gateway config; consider a fallback header toggle during transition.{ "message": ..., "errors": ... } disappearing is a breaking change. Mitigation: route-group scoping and an API version bump.crell's ProblemException thrown but not mapped in Laravel's handler → generic 500 with Laravel's default shape, silently violating the new contract. Mitigation: a render() method on the base exception, plus a contract test that asserts every thrown ApiProblemException in the test suite renders as application/problem+json.composer update breaks imports at compile time (loud, not silent) — acceptable failure mode.Ramp-up
type, title, status, detail, instance) and the difference between "problem" and "exception."type URI taxonomy and the masking policy. Budget product/API-design time for that, not engineering time.TPM verdict
Adopt as a value-object schema authority, not as an HTTP middleware. The low score reflects adoption, not quality; the package is small, spec-correct, and cheaply forkable, which materially de-risks the bus-factor concern. The real integration risk sits in our exception handler and our error-contract governance, both of which we control. Reject the PSR-15 middleware path unless the stack already carries PSR-7/15.
How can I help you explore Laravel packages today?