The HTTP QUERY method, defined in RFC 10008, is a safe, cacheable request method that carries query parameters in the body rather than the URL, giving POST-like payload room while keeping GET-style read-only semantics. Laravel 13.19 added Http::query() to the HTTP client and the queryJson() test helper; Laravel 14 will ship a first-class Route::query() helper, but until then Route::match(['QUERY'], ...) already works.

The tutorial starts from a fresh Laravel 13 application, installs the API route file and Scout, then sets the database driver in .env so Scout uses WHERE LIKE queries against the existing database. It creates an Article model, migration, factory, adds the Searchable trait, and overrides toSearchableArray() to expose only the title and body columns.

The route is registered in routes/api.php with Route::match(['QUERY'], '/articles/search', ...) and a closure that validates search as a required string and per_page as an optional integer between 1 and 50. It then calls Article::search($validated['search'])->paginate($validated['per_page'] ?? 15). Because QUERY is treated like POST for input handling, $request->input() and validation read the JSON body directly. The route list shows QUERY api/articles/search, and clients send a JSON body such as {"search": "scout", "per_page": 10}.

Tests use the queryJson() helper introduced in Laravel 13.19; an example uses RefreshDatabase, factories to seed articles, and asserts that the endpoint returns the matching article and validates the search parameter. On the consumer side, the Laravel HTTP client can call the endpoint with Http::acceptJson()->query('https://example.com/api/articles/search', ['search' => 'scout']).

One practical gotcha is that PHP's built-in development server rejects the QUERY method with a 501 response before Laravel sees it, so php artisan serve cannot be used. Nginx-based environments such as Laravel Herd and Valet pass the method through. This problem does not affect Laravel's HTTP testing helpers, which bypass the server.

If the route is placed in routes/web.php, Laravel 13's PreventRequestForgery middleware will demand a CSRF token because it only treats HEAD, GET, and OPTIONS as read methods. Until Laravel 14 exempts QUERY by default, the tutorial presents two fixes. The first is to extend PreventRequestForgery, override isReading() to also include QUERY, and replace the base middleware in the web group via bootstrap/app.php. The second is to call withoutMiddleware(PreventRequestForgery::class) on a single QUERY route. The article recommends the middleware override because it is a small, clean upgrade path. Tests will not trigger the 419 error unless the environment is changed from testing, because the CSRF middleware skips verification in that environment. Either way, QUERY handlers must stay read-only, just like GET handlers.

Before production, the article warns that the framework is only one part of the stack. CDNs, WAFs, load balancers, or proxy servers may block an unrecognized verb. Browsers support fetch() with QUERY, but HTML forms cannot submit it, and QUERY is not a CORS-safelisted method, so cross-origin requests will be preflighted. Although QUERY is defined as cacheable, browsers and CDNs do not yet implement QUERY caching. OpenAPI only added a native query operation in version 3.2, released in September 2025, so older generators and SDKs may not describe it. The conclusion is that an internal API with control over both client and server is the safest place to experiment with QUERY today.

When Laravel 14 is released, the Route::match(['QUERY'], ...) call can be replaced with Route::query(...) and the CSRF middleware override can be removed; the rest of the code remains unchanged.