Skip to content

[13.x] Add prefersJsonResponses() to the application builder#59753

Merged
taylorotwell merged 4 commits into13.xfrom
prefers-json
Apr 20, 2026
Merged

[13.x] Add prefersJsonResponses() to the application builder#59753
taylorotwell merged 4 commits into13.xfrom
prefers-json

Conversation

@WendellAdriel
Copy link
Copy Markdown
Member

@WendellAdriel WendellAdriel commented Apr 17, 2026

API-first Laravel apps regularly hit a papercut: clients sending Accept: */* (curl, most mobile SDKs, many HTTP libraries) get HTML redirects for auth failures, HTML error pages for unhandled exceptions, and HTML validation responses. Existing workarounds — custom middleware, setRequestFormat('json'), overriding the exception handler's shouldReturnJson — all work, but none are a one-liner.

This adds an opt-in Application::configure()->prefersJsonResponses() on the application builder. When enabled, it prepends a PrefersJsonResponses middleware to the global HTTP stack which rewrites Accept to application/json when the incoming header expresses no specific preference (missing, empty, or every listed entry is */* or application/*). Mixed lists that include a specific media-type are left alone so the client's preference still wins. The original header value is preserved on X-Original-Accept.

Default behavior is unchanged — this only activates when you call it.

Example

Enable it in bootstrap/app.php:

<?php

use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
    )
    ->prefersJsonResponses()
    ->create();

With that one call in place, a broad Accept header is treated as JSON:

# Returns HTML before, JSON now
curl -i http://localhost/api/users/1

# Still returns HTML — client's preference wins
curl -i -H 'Accept: text/html' http://localhost/

# Validation failures come back as JSON instead of a redirect
curl -i -X POST http://localhost/register -d 'email=bad'

Pass false to disable conditionally (e.g. based on environment):

->prefersJsonResponses($app->environment() !== 'local')

The original Accept value is preserved on X-Original-Accept in case you need it for logging or observability.

@WendellAdriel WendellAdriel marked this pull request as ready for review April 17, 2026 17:27
@cosmastech
Copy link
Copy Markdown
Contributor

It's funny you PR'd this today. I was just thinking today how every project I have worked on with Laravel needs this functionality and every project just had its own version of something like this.

@rcerljenko
Copy link
Copy Markdown

Nice idea. Would it make sense to apply it only on api routes and leave the web routes as-is?

@WendellAdriel
Copy link
Copy Markdown
Member Author

@cosmastech exactly, I already worked in many projects, each one doing its own way for this!

I think this approach is simple enough to make it easy for everyone creating API-only apps!

@WendellAdriel
Copy link
Copy Markdown
Member Author

@rcerljenko the idea for this to be a global thing is that this is aimed at API-only Laravel apps

@timacdonald
Copy link
Copy Markdown
Member

🙏

@taylorotwell taylorotwell merged commit e2ffb2c into 13.x Apr 20, 2026
37 of 53 checks passed
@taylorotwell taylorotwell deleted the prefers-json branch April 20, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants