Return JSON response for API errors in Laravel

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 API requires JSON data so it can be used on the app side.

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. If data is found then both work properly. 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.

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 requires JSON to be set into request then it will return our custom JSON response otherwise it will return a rendered view.

Further, we have created an array that contains status, code, and message based on error.

In addition, we have checked whether debug mode is enabled for the application or not. If debug mode is enabled then it will add an exception full trace with the response so the developer can debug the cause of 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.