Eran las 2:14 de la madrugada y la línea del log era aburrida de esa forma que te arruina la noche: un cliente móvil había sufrido un timeout en POST /api/jobs/search, decidió que no podía reintentar un POST con seguridad y le mostró al usuario una pantalla vacía. No se escribió nada, no se cobró nada, no se tocaron los datos de nadie. El endpoint era una lectura. Simplemente le habíamos dicho a cada pieza de software entre el teléfono y nuestra base de datos que quizá no lo era, y todas nos creyeron.
<?php
use App\Http\Controllers\Api\SearchProductsController;
use Illuminate\Support\Facades\Route;
Route::query('/products/search', SearchProductsController::class);Ese es el lío que Laravel 13.35 por fin me deja limpiar como es debido. La versión añade Route::query(), aportado por Joel Butcher en el pull request #61797, que registra una ruta para el método HTTP QUERY. El IETF publicó ese método como RFC 10008 en junio de 2026, como Proposed Standard, y su razón de ser es una petición que lleva cuerpo sin dejar de ser segura e idempotente. Mi postura es sencilla: si estás construyendo un endpoint nuevo cuyos filtros no caben cómodamente en una URL, usa QUERY ya. No cuando el ecosistema se haya puesto al día. Ya.
La parte del framework ya estaba casi hecha, y por eso siento que este es el momento. Desde la 13.19 podías enviar peticiones QUERY con Http::query() y probarlas en los tests con los helpers query() y queryJson(). Lo que faltaba era que el router hablara el verbo de forma nativa; tenías que escribirlo a mano con Route::match(['QUERY'], ...), que funciona pero se lee como un apaño. Con la 13.35 el verbo entra en la lista del router, así que Route::any() y Route::redirect() también lo recogen, y declarar el endpoint es una sola línea honesta en routes/api.php, como ves más abajo.
Voy a darle al otro bando todo su peso, porque es un argumento serio y no una queja de cascarrabias. Mucha infraestructura no ha oído hablar nunca de QUERY. Una configuración de balanceador de carga escrita en 2019, un WAF con una lista de métodos permitidos, un API gateway con un desplegable que se acaba en PATCH: cualquiera de ellos puede convertir en silencio tu elegante búsqueda en un 405. Los navegadores desde otro origen enviarán un preflight, porque QUERY no está en la lista segura de CORS. Y la historia de la caché no es el regalo que te da GET, ya que la clave de caché tiene que incluir el cuerpo de la petición, algo que tu CDN casi seguro no hará por su cuenta. Si metes la ruta en el grupo de middleware web, la comprobación CSRF de Laravel la trata como un POST, así que necesitas un token o una entrada en except. Es una lista de tareas muy real.
Aun así, me quedo con QUERY, y te explico por qué. Cada punto de esa lista es un sitio donde tu stack ya estaba tomando decisiones sobre tu tráfico de búsqueda sin que te dieras cuenta. POST /search nunca se cacheaba de todos modos, nunca se reintentaba de todos modos, y el preflight ya ocurría desde el momento en que pusiste un content type JSON. Cambiar el verbo no crea esos costes; los hace visibles y, por primera vez, arreglables, porque ahora es el propio método el que le dice al proxy que la petición es una lectura. Descubrir que tu gateway descarta los verbos desconocidos un martes por la tarde en staging es un regalo comparado con enterarte por el busca.
Hay además una ventaja más discreta que me importa más que la caché. El RFC señala que las URLs tienen más probabilidades de acabar en los logs que los cuerpos de las peticiones. Nuestros filtros de búsqueda de empleo incluyen expectativas salariales y, para algunos clientes, la empresa actual del candidato. No me hacía ninguna gracia verlos en los access logs como query strings, y me gustaba todavía menos esconderlos dentro de un POST, porque hacía que una lectura pareciera una escritura para cualquier auditor que echara un vistazo rápido a nuestras rutas. QUERY permite que el payload siga siendo más o menos privado y que la intención siga siendo legible.
Nada de esto significa que GET esté en apuros. Un número de página y una sola palabra clave pertenecen a una URL que puedes pegar en Slack, guardar en marcadores y dejar en manos de cualquier caché del planeta. A PHP siempre se le ha dado bien eso, y $_GET no se va a ninguna parte. QUERY es para el endpoint al que se le ha quedado pequeña la barra de direcciones: rangos de precio anidados, arrays de IDs, booleanos tipados que una query string aplanaría hasta convertirlos en el string "1". Mantén una ruta de respaldo para los clientes que todavía no pueden enviar el verbo, y trata la auditoría de infraestructura como parte de la funcionalidad, no como un obstáculo para ella.
Así que tengo curiosidad por saber dónde te vas a topar primero con el muro. Si pusieras una ruta QUERY delante de tu stack de producción esta semana, ¿qué capa crees que la rechazaría: el balanceador de carga, el WAF, la CDN o algún SDK que tu equipo de frontend jura que funciona bien? Y si ya lo has probado, ¿qué tuviste que cambiar para que el cuerpo se cacheara correctamente?




Comentarios
Aún no hay comentarios — escribe el primero.
Inicia la conversación
Sin cuenta ni contraseña — introduce tu correo y te enviamos un enlace de acceso de un solo uso. ¿Primera vez? Todo se configura automáticamente.
Tu valoración se aplicará automáticamente al iniciar sesión.
Revisa tu bandeja de entrada
Hemos enviado un enlace de acceso a …. Ábrelo en este dispositivo — esta pestaña te conectará automáticamente.
¿No llega nada? Mira en la carpeta de spam — y marca el correo como «No es spam» para que la próxima vez llegue directo.