jeromejhipolito / laravel-global-error-formatter
Global error/exception handler for Laravel APIs. Standardized JSON error responses with configurable exception mappings.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/jeromejhipolito/laravel-global-error-formatter
Requires
- php: ^8.2
- illuminate/http: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- illuminate/validation: ^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0
- pestphp/pest: ^2.0|^3.0
README
Global error/exception handler for Laravel APIs. Provides standardized JSON error responses with configurable exception mappings.
Features
- Global exception handling for all API errors
- Consistent JSON error response format
- Configurable exception mappings (add your own exceptions)
- Built-in handling for common Laravel exceptions
- Customizable response structure
- First-error or all-errors validation format
- Environment-based debug info (trace & error details)
- Translation support for error messages
- Base FormRequest class for consistent validation errors
Requirements
- PHP 8.2+
- Laravel 11.0+ or 12.0+
Installation
composer require jeromejhipolito/laravel-global-error-formatter
The package will auto-register its service provider.
Configuration
Publish the configuration file:
php artisan vendor:publish --tag=global-error-formatter-config
Quick Start
Register in bootstrap/app.php
use JeromeJHipolito\GlobalErrorFormatter\GlobalErrorFormatter; return Application::configure(basePath: dirname(__DIR__)) ->withExceptions(function (Exceptions $exceptions) { GlobalErrorFormatter::register($exceptions); }) ->create();
That's it! All exceptions in API requests will now return consistent JSON responses.
Response Format
{
"status": "error",
"message": "Resource not found.",
"errors": null,
"trace": null
}
Environment-Based Error Details
The package automatically shows/hides error details based on your environment:
// config/global-error-formatter.php // Stack trace - only shown in debug mode 'include_trace' => env('GLOBAL_ERROR_INCLUDE_TRACE', env('APP_DEBUG', false)), // Error details (exception message) - only shown in debug mode 'include_error_details' => env('GLOBAL_ERROR_DETAILS', env('APP_DEBUG', false)),
Development (APP_DEBUG=true):
{
"status": "error",
"message": "Something went wrong.",
"errors": "SQLSTATE[42S02]: Base table or view not found...",
"trace": [...]
}
Production (APP_DEBUG=false):
{
"status": "error",
"message": "Something went wrong."
}
Adding Custom Exceptions
Via Configuration
// config/global-error-formatter.php 'exceptions' => [ \App\Exceptions\PaymentException::class => [ 'status' => 402, 'message' => 'Payment required.', ], \App\Exceptions\CouponException::class => [ 'status' => 400, 'message' => null, // Uses exception's getMessage() ], ],
Programmatically
use JeromeJHipolito\GlobalErrorFormatter\GlobalErrorFormatter; return Application::configure(basePath: dirname(__DIR__)) ->withExceptions(function (Exceptions $exceptions) { $formatter = app(GlobalErrorFormatter::class); $formatter ->addException(PaymentException::class, 402, 'Payment required.') ->addException(CouponException::class, 400); // Uses exception message $exceptions->render(function (Throwable $e, Request $request) use ($formatter) { if ($request->expectsJson()) { return $formatter->format($e); } return null; }); }) ->create();
Built-in Exception Handling
| Exception | Status | Default Message |
|---|---|---|
ModelNotFoundException |
404 | Resource not found. |
NotFoundHttpException |
404 | Route not found. |
AuthenticationException |
401 | Unauthenticated. |
AuthorizationException |
403 | Unauthorized. |
ValidationException |
422 | First validation error |
ThrottleRequestsException |
429 | Too many requests. |
| Other exceptions | 500 | Something went wrong. |
Using BaseFormRequest
Extend BaseFormRequest for consistent validation error responses:
use JeromeJHipolito\GlobalErrorFormatter\BaseFormRequest; class CreateUserRequest extends BaseFormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'email' => 'required|email|unique:users', 'password' => 'required|min:8', ]; } }
Validation errors will automatically use the configured format.
Configuration Options
Response Keys
Customize the JSON response structure:
'response_keys' => [ 'status' => 'status', // Change to 'result', 'success', etc. 'message' => 'message', // Change to 'msg', 'error', etc. 'errors' => 'errors', // Change to 'details', 'data', etc. 'trace' => 'trace', // Change to 'stack', 'debug', etc. ],
Status Values
Customize the status field values:
'status_values' => [ 'success' => 'success', 'error' => 'error', // Change to 'failed', 'failure', etc. ],
Validation Format
Choose how validation errors are returned:
// 'first' - Only the first error message (default) 'validation_format' => 'first', // 'all' - All errors with field names 'validation_format' => 'all',
First format response:
{
"status": "error",
"message": "The email field is required."
}
All format response:
{
"status": "error",
"message": "Validation failed.",
"errors": {
"email": ["The email field is required."],
"password": ["The password must be at least 8 characters."]
}
}
Translation Support
// Pass messages through trans() helper 'translate_messages' => true,
This allows you to create translation files for error messages:
// lang/ja/messages.php return [ 'Resource not found.' => 'リソースが見つかりません。', 'Validation failed.' => '検証に失敗しました。', ];
Conditional Registration
Only handle specific requests:
GlobalErrorFormatter::register($exceptions, function (Throwable $e, Request $request) { // Only handle API routes return $request->is('api/*'); });
Override Default Messages
'defaults' => [ 'model_not_found' => [ 'status' => 404, 'message' => 'The requested item was not found.', ], 'authentication' => [ 'status' => 401, 'message' => 'Please log in to continue.', ], // ... other defaults ],
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.