Laravel API 404 Response: Return JSON Instead of Webpage Error

If you're building a Laravel project with both Web and API sides, you need to customize error messages for each of them separately. In web-view there should be error pages, and API exceptions should return JSON with status codes. How to handle it? I will show you an example with case of Model Not Found 404.

Imagine this scenario - a web page user profile, and API-based similar request.

So, we have app/Http/Controllers/UserController.php:

public function show(User $user)
{
    return view('users.show', compact('user'));
}

And then we have API - in app/Http/Controllers/API/V1/UserController.php:

public function show(User $user)
{
    return $user;
}

The second example will just return JSON'ed Collection, without any web template. And that's fine.

The problem starts when you try to load that API call and the user is not found. Since we're using Route Model Binding here and passing (User $user) directly, under the hood it's launching User::findOrFail($id) method and throws exception of ModelNotFoundException and shows 404 page.

In API side, you also get an error page instead of JSON error:

Screenshot from a Postman request

So, our goal is to stay as it is for the web version, but override the API error handling to return a proper JSON.

To do that, we need to add this logic to the app/Exceptions/Handler.php class:

use Illuminate\Database\Eloquent\ModelNotFoundException;

// ...

public function render($request, Exception $exception)
{
    if ($exception instanceof ModelNotFoundException && $request->wantsJson()) {
        return response()->json(['message' => 'Not Found!'], 404);
    }

    return parent::render($request, $exception);
}

And that's it, you will receive a well-formed response in Postman:

And it won't change the default behavior for web-based requests, it will still show good old 404 page.

For more tips how to handle API errors and exceptions, see my other in-depth article.

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month

Written by

You might also like