My suggestion is to stick to standard Laravel responses until you really know why you need a custom structure. Otherwise you can spend a lot of time reinventing the wheel while clients of your API don't need it at all.
Simple abort(404, 'I haven\'t found anything'); will give you nice response:
HTTP/1.1 404 Not Found
{
"message": "I haven't found anything"
}
If you have APP_DEBUG=true in .env this JSON will be appended with stack trace which is useful for debugging. That's all, do you really need something more complex?
If you want to stick to your structure, I would go with something like this:
class ExceptionSchema
{
public static function render (\Throwable $exception, Request $request): JsonResponse
{
return response()->json((new self)->jsonSchema($exception, $request));
}
public function jsonSchema (\Throwable $exception, Request $request): array
{
return [
'success' => false,
'type' => $this->handleType($exception),
'statusCode' => $this->handleStatusCode($exception),
'message' => $exception->getMessage(),
'data' => null,
'errors' => [
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTrace(),
],
'meta' => [
'controller' => $request->route()->getControllerClass(),
'action' => $request->route()->getActionName(),
'parameters' => $request->route()->parameters() ?? null,
],
'timestamp' => Carbon::now()->toDateTimeString(),
];
}
public function handleType (\Throwable $exception): ?string
{
return match (get_class($exception)) {
NotFoundHttpException::class => 'MY_CUSTOM_TYPE_FOR_404_ERROR',
AccessDeniedHttpException::class => 'MY_CUSTOM_TYPE_FOR_403_ERROR',
// ...
default => null,
};
}
public function handleStatusCode (\Throwable $exception): int
{
return match (get_class($exception)) {
NotFoundHttpException::class => 404,
AccessDeniedHttpException::class => 403,
// ...
default => 500,
};
}
}
After that use it in app.php:
$exceptions->render(ExceptionSchema::render(...));
But again, Laravel already does the same inside.