Exception Handling
The toolkit provides an exception handler that renders all exceptions as JSON:API error responses, so your API never returns HTML error pages.
Setup
Register the handler in bootstrap/app.php:
use BlueBeetle\ApiToolkit\Exceptions\ConfigureExceptionHandler;
->withExceptions(new ConfigureExceptionHandler())
Exception Mapping
| Exception | Status | Title |
|---|---|---|
AuthenticationException | 401 | Unauthorized |
ValidationException | 422 | Validation Error (with field pointers) |
ModelNotFoundException | 404 | Not Found |
NotFoundHttpException | 404 | Not Found |
MethodNotAllowedHttpException | 404 | Not Found |
RouteNotFoundException | 404 | Not Found |
QueryException | 400 | Bad Request |
LazyLoadingViolationException | 400 | Bad Request |
IdempotencyException | varies | Idempotency Error |
| Domain exceptions (configurable) | 400 | Bad Request |
| Everything else | 500 | Internal Server Error |
Validation Errors
Validation exceptions are rendered with source pointers for each field:
{
"errors": [
{
"status": "422",
"code": "validation_error",
"title": "Validation Error",
"detail": "The name field is required.",
"source": {
"pointer": "/name"
}
},
{
"status": "422",
"code": "validation_error",
"title": "Validation Error",
"detail": "The email must be a valid email address.",
"source": {
"pointer": "/email"
}
}
]
}
Domain Exceptions
Register your application's domain exceptions so they're treated as 400 errors instead of 500s:
// config/api-toolkit.php
'exceptions' => [
'domain' => [
\App\Exceptions\InsufficientFundsException::class,
\App\Exceptions\OrderAlreadyShippedException::class,
],
],
Debug Mode
When APP_DEBUG is true, error responses include additional debug information:
{
"errors": [
{
"status": "500",
"code": "api_error",
"title": "Internal Server Error",
"detail": "SQLSTATE[42S22]: Column not found...",
"meta": {
"debug": {
"line": 42,
"file": "/app/Http/Controllers/ProductController.php",
"class": "Illuminate\\Database\\QueryException",
"trace": [...]
}
}
}
]
}
Suppressing Reports
Prevent certain exceptions from being logged:
// config/api-toolkit.php
'exceptions' => [
'dont_report' => [
\App\Exceptions\ExpectedBusinessException::class,
],
],
Error Response Format
All error responses follow the same JSON:API structure. Each field is included only when available:
{
"errors": [
{
"status": "400",
"code": "invalid_request_error",
"title": "Bad Request",
"detail": "A human-readable explanation of the error.",
"source": {
"pointer": "/field"
},
"meta": {
"debug": { "..." }
}
}
]
}
| Field | Always present | Description |
|---|---|---|
status | Yes | HTTP status code as a string |
code | Yes | Machine-readable error category |
title | Yes | Short summary of the error |
detail | Yes | Specific explanation for this occurrence |
source.pointer | Validation only | JSON pointer to the field that caused the error |
meta.debug | Debug mode only | Line, file, class, and stack trace |
Error Codes
| Code | Used for |
|---|---|
invalid_request_error | Authentication, not found, bad request, HTTP errors |
validation_error | Validation failures (422) |
idempotency_error | Idempotency conflicts |
api_error | Unhandled exceptions (500) |