Force JSON response on all API routes in Laravel
Tags: laravel, php, webdev
Published: 2021-03-23 10:01:27

Laravel often returns an HTML response or redirect response on API routes, mostly if the response is an error one. However, it is quite easy to force Laravel to return JSON response in those cases.

> 🇨🇿 V češtině si lze článek přečíst na [kutac.cz](https://www.kutac.cz/weby-a-vse-okolo/laravel-vynuceni-json-response-pro-api)

---

You can simulate the described behavior by adding the code below into the `routes/api.php` file and doing requests in Postman or browser. On the image, you can see some HTML responses in such cases.

```php
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('health-check', function () {
    return response()->json([ 'status' => 'OK', 'timestamp' => Carbon::now() ]);
});
Route::post('settings', function (Request $request) {
    $request->validate([ 'entry' => 'required|string|min:5' ]);
    return 'OK';
});
```

[![Examples of HTML responses on API routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41tlvn5z45n38a7pes3p.png)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41tlvn5z45n38a7pes3p.png)

# How to force JSON response

Laravel checks the `Accept` header in the request. Then deciding according to this header, which response it should send back. But browser or Postman often sends `Accept: */*`. So it is enough to rewrite this header and the rest will be handled by Laravel.

## Custom middleware and route fallback

Middleware is the best friend to achieve this. First, create the file **app/Http/Middleware/ForceJsonResponse.php** or use `php artisan make:middleware ForceJsonResponse`. Second, register the created middleware in **app/Http/Kernel.php**. The last step, adding the fallback route to **routes/api.php** is optional but recommended.

```php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ForceJsonResponse
{
    public function handle(Request $request, Closure $next)
    {
        $request->headers->set('Accept', 'application/json');
        return $next($request);
    }
}
```

```php
namespace App\Http;

class Kernel extends \Illuminate\Foundation\Http\Kernel
{
    ...
    protected $middlewareGroups = [
        ...
        'api' => [
            \App\Http\Middleware\ForceJsonResponse::class,
            ...
        ],
    ];
}
```

```php
// routes/api.php
Route::fallback(function (){
    abort(404, 'API resource not found');
});
```

## There can be still HTML response 

The code will solve JSON responses in error cases. But Laravel returns HTML if the controller is returning a string. For example 3 on the image above will be still `text/html` response. This can be handled by middleware as well. It is described on [DarkGhostHunter](https://darkghosthunter.medium.com/laravel-convert-to-json-all-responses-automatically-c4a72b2fd3ac) blog. But I don't like this solution and I prefer returning JSON response directly. So, update the code like this:

```php
Route::post('settings', function (Request $request) {
    $request->validate([ 'entry' => 'required|string|min:5' ]);
    // return 'OK';
    return response()->json('OK');
});
```