Es war 2:14 Uhr nachts, und die Logzeile war auf genau die langweilige Art, die dir die Nacht ruiniert: Ein mobiler Client war bei POST /api/jobs/search in einen Timeout gelaufen, hatte beschlossen, dass er einen POST nicht gefahrlos wiederholen darf, und dem Nutzer einen leeren Bildschirm gezeigt. Nichts wurde geschrieben, nichts abgebucht, niemandes Daten wurden angefasst. Der Endpoint war ein reiner Lesezugriff. Wir hatten nur jeder Software zwischen Handy und Datenbank erzählt, dass er vielleicht keiner ist, und alle haben uns geglaubt.
<?php
use App\Http\Controllers\Api\SearchProductsController;
use Illuminate\Support\Facades\Route;
Route::query('/products/search', SearchProductsController::class);Genau dieses Chaos kann ich mit Laravel 13.35 endlich ordentlich aufräumen. Das Release bringt Route::query(), beigesteuert von Joel Butcher in Pull Request #61797, und registriert damit eine Route für die HTTP-Methode QUERY. Die IETF hat diese Methode im Juni 2026 als RFC 10008 veröffentlicht, als Proposed Standard, und ihr ganzer Sinn ist ein Request, der einen Body mitbringt und trotzdem safe und idempotent bleibt. Meine Position ist einfach: Wenn du einen neuen Endpoint baust, dessen Filter nicht bequem in eine URL passen, greif jetzt zu QUERY. Nicht erst, wenn das Ökosystem aufgeholt hat. Jetzt.
Auf Framework-Seite war das meiste schon erledigt, deshalb fühlt sich das wie der richtige Moment an. Seit 13.19 konntest du QUERY-Requests mit Http::query() verschicken und sie in Tests über die Helper query() und queryJson() durchspielen. Was fehlte, war ein Router, der das Verb von Haus aus spricht. Du musstest es mit Route::match(['QUERY'], ...) ausbuchstabieren, was zwar funktioniert, sich aber wie ein Workaround liest. Mit 13.35 steht das Verb in der Liste des Routers, also nehmen es auch Route::any() und Route::redirect() mit, und die Deklaration des Endpoints ist eine ehrliche Zeile in routes/api.php, wie unten zu sehen.
Ich will der Gegenseite ihr volles Gewicht lassen, denn das ist ein ernsthaftes Argument und kein bloßes Gemecker. Jede Menge Infrastruktur hat noch nie von QUERY gehört. Eine Load-Balancer-Konfiguration aus dem Jahr 2019, eine WAF mit einer Allowlist für Methoden, ein API-Gateway mit einem Dropdown, das bei PATCH aufhört: Jedes davon kann deine elegante Suche stillschweigend in einen 405 verwandeln. Browser auf einem anderen Origin schicken einen Preflight, weil QUERY nicht auf der CORS-Safelist steht. Und beim Caching gibt es nicht das Gratis-Mittagessen, das du von GET kennst, denn der Cache-Key muss den Request-Body enthalten, und das macht dein CDN so gut wie sicher nicht von allein. Wenn du die Route in die web-Middleware-Gruppe packst, behandelt Laravels CSRF-Prüfung sie wie einen POST, du brauchst also ein Token oder einen except-Eintrag. Das ist eine echte Liste an Aufgaben.
Warum ich trotzdem bei QUERY lande: Jeder Punkt auf dieser Liste ist eine Stelle, an der dein Stack schon längst über deinen Such-Traffic entschieden hat, ohne dass du es gemerkt hast. POST /search wurde ohnehin nie gecacht, ohnehin nie wiederholt, und der Preflight lief sowieso, sobald du einen JSON-Content-Type gesetzt hast. Der Wechsel des Verbs erzeugt diese Kosten nicht. Er macht sie sichtbar und zum ersten Mal behebbar, weil die Methode selbst dem Proxy jetzt sagt, dass es sich um einen Lesezugriff handelt. An einem Dienstagnachmittag im Staging herauszufinden, dass dein Gateway unbekannte Verben verwirft, ist ein Geschenk im Vergleich dazu, es vom Pager zu erfahren.
Dazu kommt ein leiserer Gewinn, der mir sogar wichtiger ist als das Caching. Der RFC weist darauf hin, dass URLs eher in Logs landen als Request-Bodys. Unsere Filter für die Jobsuche enthalten Gehaltsvorstellungen und bei manchen Kunden den aktuellen Arbeitgeber eines Kandidaten. Die als Query-Strings in Access-Logs zu sehen, fand ich nicht toll, und sie in einem POST zu verstecken, gefiel mir noch weniger, weil dadurch jeder Lesezugriff für alle Auditoren, die unsere Routen überflogen, wie ein Schreibzugriff aussah. Mit QUERY bleibt die Payload halbwegs privat und die Absicht trotzdem lesbar.
Das alles heißt nicht, dass GET in Schwierigkeiten steckt. Eine Seitenzahl und ein einzelnes Suchwort gehören in eine URL, die du in Slack einfügen, als Lesezeichen speichern und von jedem Cache der Welt verarbeiten lassen kannst. Darin war PHP schon immer gut, und $_GET verschwindet nicht. QUERY ist für den Endpoint gedacht, der der Adresszeile entwachsen ist: verschachtelte Preisspannen, Arrays von IDs, typisierte Booleans, die ein Query-String zum String „1“ plattdrücken würde. Behalte eine Fallback-Route für Clients, die das Verb noch nicht senden können, und betrachte den Infrastruktur-Check als Teil des Features, nicht als Hindernis dafür.
Mich interessiert also, wo du zuerst gegen die Wand läufst. Wenn du diese Woche eine QUERY-Route vor deinen Produktions-Stack stellst, welche Schicht wird sie deiner Erwartung nach ablehnen: der Load Balancer, die WAF, das CDN oder irgendein SDK, bei dem dein Frontend-Team schwört, dass alles passt? Und falls du es schon ausprobiert hast: Was musstest du ändern, damit der Body korrekt gecacht wird?




Kommentare
Noch keine Kommentare — schreib den ersten.
Starte die Diskussion
Kein Konto, kein Passwort nötig — gib einfach deine E-Mail-Adresse ein, wir senden dir einen einmaligen Anmelde-Link. Beim ersten Mal bist du damit automatisch angemeldet.
Deine Bewertung wird nach der Anmeldung automatisch übernommen.
Schau in dein Postfach
Wir haben einen Anmelde-Link an … gesendet. Öffne ihn auf diesem Gerät — dieser Tab meldet dich automatisch an.
Nichts angekommen? Wirf einen Blick in den Spam-Ordner — und markiere die Mail dort als „Kein Spam“, dann landet sie künftig direkt im Postfach.