Laravel provides rich error-handling mechanisms but while working with APIs we need to get error responses into JSON data instead of error pages.
While building a Laravel application for the web as well as APIs we need to place different logic for some endpoints while getting users, in the web we need to render a view into API we just need to pass user data. The same thing happens for errors, but APIs require JSON data so the app can use it.
In this tutorial, we will customize the error response for API. In Laravel, error handling renders all the errors through one functionality and we will modify it as per our requirements.
Before starting let’s take an example, to get user data into the web and API.
We have created a show method at app/Http/Controllers/UserController.php for the web which looks like the below:
public function show(User $user)
{
return view('users.show', compact('user'));
}
For API we have created a show method at app/Http/Controllers/API/UserController.php:
public function show(User $user)
{
return response()->json('user');
}
In both examples, we have bound the model to a method. which will use findOrFail() method on the User model. Both works properly if data is found. But if an error occurs then it will show a view to the user which is good for the web but it will not work for API.
Managing API Error Response In JSON
that error can be any type so let’s customize the error response for our application. Here, we will check request is coming from API or the web and return a response based on that condition.
To do that, we need to add this logic to the app/Exceptions/Handler.php class. Open that file and modify it as below:
<?php
namespace App\Exceptions;
use Throwable;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
public function render($request, Exception $e)
{
if( ($request->is('api/*') || $request->wantsJson() ) && !($e instanceof ValidationException)){
$response = [
'message' => (string)$e->getMessage(),
'status_code' => 400,
];
if ($e instanceof HttpException) {
$response['message'] = Response::$statusTexts[$e->getStatusCode()];
$response['status_code'] = $e->getStatusCode();
} else if ($e instanceof ModelNotFoundException) {
$response['message'] = Response::$statusTexts[Response::HTTP_NOT_FOUND];
$response['status_code'] = Response::HTTP_NOT_FOUND;
}
if ($this->isDebugMode()) {
$response['debug'] = [
'exception' => get_class($e),
'trace' => $e->getTrace()
];
}
return response()->json([
'status' => 'failed',
'status_code' => $response['status_code'],
'massage' => $response['message'],
], $response['status_code']);
}
return parent::render($request, $e);
}
}
Here, first of all, we checked request is coming from API or request wants JSON as a response. If the request expects JSON, Laravel returns our custom JSON response; otherwise, it returns a rendered view.
Further, we have created an array that contains status, code, and message based on error.
In addition, we check if debug mode is enabled for the application. When it is, the response includes the full exception trace so the developer can debug the error.
Conclusion
In this article, we have customized error responses for APIs or AJAX requests that accept JSON as the response. While developing APIs you need to provide a proper response even if it’s an error.
You can also enhance your workflow by learning how to seed JSON data into your database. Check out our guide on How to Seed JSON Data in Laravel? for step-by-step examples.