Back to Course |
How to Build Laravel 11 API From Scratch

Why 404 Page? Setting Correct Headers

In this lesson, we will set one correct header value to avoid returning web pages instead of JSON results.


As you can see in the /api/categories/1, we see data for one category.

But what happens if we pass a missing category? We get an HTML page.

But for API, it is wrong. API expects the result as a JSON. In your client, set the headers to Accept -> application/json. Now Laravel knows to send the result as a JSON.

Another way is to set headers using Middleware. First, let's create a Middleware.

php artisan make:middleware AlwaysAcceptJson

Inside Middleware, we have a Request. Middleware is not just for preventing something; you can also add something to that request and pass that to the next request.

app/Http/Middleware/AlwaysAcceptJson.php:

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

Now, we must register Middleware. Middleware can be registered to every Route or prepented to group web or api.

bootstrap/app.php:

return Application::configure(basePath: dirname(__DIR__))
->withProviders()
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->prependToGroup('api', \App\Http\Middleware\AlwaysAcceptJson::class);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

As you can see, with the disabled header in the client, we still get the same error in JSON format.

When working with APIs, remember to set headers.


Customize Error Message

When the error message is received, in this example, we have a message for the "Model not found".

No query results for model [App\\Models\\Category] 13

The [App\\Models\\Category] part could be a security risk because a potential hacker could guess this is a Laravel project. Let's overwrite this message.

Exception can be added in the bootstrap/app.php file withExceptions method.

We need to render when the Exception is NotFoundHttpException. We can check the error message exception key to find which Exception to use.

We can return the custom message with a 404 status.

bootstrap/app.php:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
return Application::configure(basePath: dirname(__DIR__))
->withProviders()
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->prependToGroup('api', \App\Http\Middleware\AlwaysAcceptJson::class);
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->renderable(function (NotFoundHttpException $e) {
return response()->json(['message' => 'Object not found'], 404);
});
})->create();

We can see our custom message.

This way, if your application also has a web version, then in the browser, users will see the same message instead of an HTML 404 error. To prevent that from happening, we must make a simple check.

bootstrap/app.php:

use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->prependToGroup('api', \App\Http\Middleware\AlwaysAcceptJson::class);
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->wantsJson()) {
return response()->json(['message' => 'Object not found'], 404);
}
});
})->create();

Request is a second parameter for a renderable callback function.