app-verk/api-exception-bundle
composer require app-verk/api-exception-bundle
config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3.x):
AppVerk\ApiExceptionBundle\AppVerkApiExceptionBundle::class => ['all' => true],
config/packages/app_verk_api_exception.yaml:
app_verk_api_exception:
enabled: true
new \RuntimeException('Test error')) in a controller.application/problem+json response:
{
"detail": "Test error",
"status": 500,
"type": "about:blank",
"title": "Internal Server Error"
}
Exception Handling:
ApiExceptionSubscriber and converts them into ApiProblem objects.Customizing Responses:
ResponseFactoryInterface:
// src/Factory/CustomResponseFactory.php
class CustomResponseFactory implements ResponseFactoryInterface {
public function createResponse(ApiProblemInterface $apiProblem) {
return new JsonResponse([
'error' => $apiProblem->toArray(),
'timestamp' => now()->toIso8601String(),
]);
}
}
config/packages/app_verk_api_exception.yaml:
app_verk_api_exception:
response_factory: App\Factory\CustomResponseFactory
Excluding Paths:
paths_excluded:
app_verk_api_exception:
paths_excluded: ['/admin/', '/_error']
symfony/serializer for complex error payloads (e.g., nested validation errors).// In ApiExceptionSubscriber
$logger->error($exception->getMessage(), ['exception' => $exception]);
ResponseFactoryInterface in unit tests to assert response formats:
$factory = $this->createMock(ResponseFactoryInterface::class);
$factory->expects($this->once())
->method('createResponse')
->willReturn(new JsonResponse(['custom' => 'data']));
$this->container->set(ResponseFactoryInterface::class, $factory);
Symfony 5+ Compatibility:
config/bundles.php (not AppKernel).config/bundles.php with all: true.Overriding Default Factory:
response_factory in config will use the default factory, which may not match your API’s error schema.Paths Exclusion:
paths_excluded are case-sensitive and must match the exact route path (including trailing slashes)./api/v1/users but not /api/v1/users/.Performance Impact:
No Response Conversion:
enabled: false in config or the subscriber is not registered (Symfony 5+ bundles.php).dump() in ApiExceptionSubscriber::onKernelException() to verify the subscriber is triggered.Custom Factory Not Loading:
src/ and namespaced correctly).php bin/console cache:clear) and check Symfony’s debug toolbar for errors.RFC7807 Compliance:
type field uses about:blank. Override it in your factory for meaningful URIs (e.g., /problems/invalid-request):
$apiProblem->setType('/api/problems/' . strtolower($exception->getMessage()));
Add Custom Fields:
ApiProblem by creating a custom class:
class CustomApiProblem extends ApiProblem {
public function setAdditionalData(array $data) {
$this->additional = $data;
}
}
$problem = new CustomApiProblem(400, 'Bad Request');
$problem->setAdditionalData(['validation_errors' => $errors]);
Conditional Handling:
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface) {
// Handle HTTP exceptions differently
}
Localization:
public function __construct(private TranslatorInterface $translator) {}
public function createResponse(ApiProblemInterface $apiProblem) {
$apiProblem->setTitle($this->translator->trans($apiProblem->getTitle()));
}
How can I help you explore Laravel packages today?