jenky/api-error-bundle
A PHP bundle for standardizing API error responses, helping you return consistent JSON error payloads across endpoints with minimal setup and configuration.
jenky/api-error-bundle is distributed as a Symfony bundle, not a Laravel package. There is no service provider, no config/api-error.php, and no facade. On a Laravel codebase your options are (a) port the concepts, or (b) use it in a Symfony service inside a monorepo/BFF layer. Check composer.json (type: symfony-bundle, require: symfony/http-kernel) before assuming composer require jenky/api-error-bundle gives you anything in config/app.php.Jenky\ApiErrorBundle\ApiErrorBundle), any DependencyInjection/Configuration.php (the only real source of truth for options), and the exception-mapping/listener classes. The README is thin; the config tree and the tests are the documentation.// config/bundles.php
return [ Jenky\ApiErrorBundle\ApiErrorBundle::class => ['all' => true] ];
Then bin/console config:dump-reference api_error (or whatever the extension alias is) to see the available knobs — do this before writing any code, because most "why is my error shape wrong" questions are answered there.Accept: application/json and inspecting both the body and the Content-Type header.0.3.x — pre-1.0. Use a caret constraint with an explicit floor ("jenky/api-error-bundle": "^0.3.1") and commit the lockfile; do not float on * or dev-master.if chains. Define a map of exception class → HTTP status + machine-readable error code, and keep controllers free of try/catch blocks that only re-shape the payload. This is the main day-to-day win: controllers throw domain exceptions, the bundle renders them.NotFoundException, ValidationFailedException, ConflictException) extending a base ApiException that carries a status code and an error code. Map the base class once; subclasses inherit. This scales far better than listing every concrete class in config.type, title, status, detail, code, plus a meta/errors bag) and have your exceptions supply only the variable parts. Consumers then get one shape for every failure, which is what makes client-side error handling possible.ConstraintViolationList-style structure. Write a dedicated mapper/normalizer that flattens violations into errors: [{ field, message }] — otherwise you get a nested object graph that leaks framework internals into your public API.Accept: application/json, */* from an XHR, or a route prefix like /api/*). Keep HTML error pages for browser traffic; a global override confuses users and breaks debug tooling.warning/info with no stack trace, and let 5xx bubble or log at error with the exception attached. Doing this in one place prevents the classic double-log problem after the listener renders the response.bootstrap/app.php (Laravel 11+) with ->withExceptions(function (Exceptions $exceptions) { ... }) and a render() method on a base ApiException. You get the ergonomics without the bundle, and RFC 7807 payloads are easy to emit with response()->json($problem, $status, ['Content-Type' => 'application/problem+json']).0.3.0...0.3.1 before bumping: on a 0.x package, behavior fixes can alter payload keys or status codes without a major-version signal. Treat every patch as potentially significant and re-run your error-shape contract tests.ApiErrorFactory/normalizer service so a future rename costs you one adapter class, not fifty call sites.framework.exceptions / the error controller can win over a listener depending on where the exception is thrown (kernel vs. controller, sub-requests, kernel.exception priority). If your mapper "sometimes" isn't called, check listener priority and whether the error is rendering through the error controller instead.APP_DEBUG=1 typically exposes class names, messages, and traces. Make sure your tests run with debug off at least once, and never let a detail field be populated from getMessage() of an unmapped exception in production — that's an information-disclosure bug waiting to happen.Content-Type must be application/problem+json (if you're claiming RFC 7807) — not application/json. Some clients and API gateways branch on this header. Set it explicitly rather than inheriting the default.\Error, \TypeError, and fatal errors bypass the exception listener in some paths. Don't assume a 500 from a fatal will be shaped like your envelope; add a fallback at the server/edge layer if the contract matters.title strings. Don't hardcode English copy inside mappers. Route through a translator or a message catalogue keyed by error code so title/detail can be localized later without touching mapping logic.match (true) over exception types in a single render() method.How can I help you explore Laravel packages today?