Metoda HTTP QUERY dla zespołów frontendowych: bezpieczne odczyty z ciałem wiadomości
Dowiedz się, kiedy metoda HTTP QUERY jest lepsza od GET i POST przy złożonych filtrach, jak używać jej z fetch, oraz jakie wymagania stawia CORS, cacheowanie i wsparcie infrastruktury.
Ekrany do wyszukiwania i filtrowania mają powtarzający się problem z API. Jeśli zakodujesz każdy filtr w łańcuchu zapytania GET, URL staje się długi i niepraktyczny; jeśli natomiast wyślesz filtry w formacie JSON za pomocą POST, używasz metody, która sygnalizuje zmianę stanu w przypadku czynności, która w rzeczywistości jest tylko odczytem. Metoda HTTP QUERY została stworzona właśnie do rozwiązania tego problemu. Ten artykuł wyjaśnia, co oznacza QUERY, jak wysyłać je z aplikacji React za pomocą fetch, oraz które elementy Twojej architektury, od CORS po pamięci cache, wymagają uwagi przed ich wykorzystaniem.
Problem, który rozwiązuje QUERY
Listing produktów z niewielką liczbą filtrów szybko generuje takie żądanie:
/products?category=laptop&brand=Apple&minPrice=50000&maxPrice=150000&rating=4
To działa, ale każdy nowy filtr wydłuża łańcuch zapytania, wartości nawiasowe lub powtarzane (kilka marek, zakres cenowy, kryteria sortowania, paginacja) wymagają specjalnych zasad kodowania, a bardzo długie URL mogą napotkać ograniczenia co do długości na serwerach, proxy’ach i w logach.
Zwykłym rozwiązaniem jest użycie metody POST z ciałem w formacie JSON. Jest to prostsze do implementacji po stronie klienta, ale utrudnia zrozumienie umowy między stronom. Metoda POST nie jest ani bezpieczna, ani idempotentna, więc pośrednicy nie mogą zakładać, że żądanie można bezpiecznie powtórzyć, cache’i domyślnie nie przechowują odpowiedzi, a każdy, kto korzysta z API, musi sprawdzić dokumentację, aby dowiedzieć się, że ta konkretna metoda POST służy wyłącznie do odczytu danych.
Czym jest QUERY
QUERY to metoda HTTP służąca do żądania od serwera wykonania zapytania opisanego w treści żądania. Zgodnie z doniesieniami z czasu pisania tego tekstu, IETF opublikował ją jako standard w czerwcu 2026 roku pod numerem RFC 10008; przed jej cytowaniem sprawdź aktualny status w indeksie RFC. Zamiast umieszczać parametry bezpośrednio w adreście URL, klient wysyła je w ciele żądania:
QUERY /products HTTP/1.1
Content-Type: application/json
{
"category": "laptop",
"brands": ["Apple", "Dell"],
"price": {
"min": 50000,
"max": 150000
}
}
Serwer analizuje zapytanie i zwraca odpowiadające mu wyniki. To, co odróżnia QUERY od POST, to ich semantyka: QUERY jest definiowany jako bezpieczny, co oznacza, że nie modyfikuje docelowej zasoby, oraz idempotentny, co oznacza, że wysłanie go dwa razy ma taki sam efekt jak wysłanie go raz. Te dwie właściwości umożliwiają klientom, proxy’om i buforom pamięci traktowanie go jak operacji odczytu. Idempotentność oznacza na przykład, że klient lub pośrednik może automatycznie spróbować ponownie zakończonej niepowodzeniem operacji QUERY bez ryzyka powstania podwójnych skutków ubocznych, czego nie powinien robić w przypadku POST.
Wybór między GET, POST a QUERY
QUERY nie zastępuje pozostałych metod. Każda z nich nadal pełni określoną rolę:
- GET pozostaje metodą domyślną do prostego pobierania danych: zasoby po ich identyfikatorze lub krótka lista z kilkoma prostymi parametrami.
Dla interfejsu użytkownika zaletą jest to, że złożone mapy stanu filtrów są bezpośrednio przekazywane w ciele żądania, a umowa API jasno stanowi, że nic nie jest zmieniane.
Wysyłanie żądania QUERY z React
Załóżmy komponent React, który przechowuje aktualne filtry produktów w obiekcie:
const filters = {
category: "laptop",
brands: ["Apple", "Dell"],
price: {
min: 50000,
max: 150000
},
rating: 4
};
W przypadku QUERY ten obiekt może zostać zserializowany i wysłany w takiej samej formie. Poniższy helper używa fetch z własną nazwą metody oraz ciałem w formacie JSON:
const searchProducts = async (filters) => {
const response = await fetch("/api/products", {
method: "QUERY",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(filters)
});
Druga część tej samej funkcji sprawdza status odpowiedzi i parsuje wynik:
if (!response.ok) {
throw new Error("Failed to fetch products");
} return response.json();
};
Poza nazwą metody, jest to identyczne z wywołaniem POST. Zmiana ma charakter semantyczny, a nie składniowy: żądanie teraz określa się jako zapytanie. Należy zwrócić uwagę na dwa praktyczne aspekty. Standard Fetch zapisuje wielkimi literami tylko kilka dobrze znanych nazw metod, dlatego zawsze należy pisać "QUERY" wielkimi literami. Różniąc się od GET i HEAD, które funkcja fetch odrzuca przy wysyłaniu z ciałem, QUERY może je zawierać.
Jeden z kompromisów dotyczy interfejsów użytkownika. Filtry zakodowane w URL można dodać do ulubionych i udostępnić, a także zachowają się po ponownym załadowaniu strony. Ciało zapytania QUERY tak nie robi. Jeśli ważne są udostępnialne linki, warto nadal odzwierciedlać stan filtrów w URL strony po stronie klienta, jednocześnie wysyłając zstrukturyzowane zapytanie do API.
Sprawdź obsługę na całej ścieżce żądania
Standaryzacja metody nie oznacza, że każdy komponent obsługujący ruch sieciowy ją rozumie. Zapytanie przeglądarki przechodzi przez kilka warstw, zanim dotrze do aplikacji, a każda z nich może odrzucić lub zniekształcić nieznaną metodę. Przed wdrożeniem QUERY sprawdź:
- Behawior przeglądarki i funkcji
fetch()w obsługiwanych przez Ciebie przeglądarkach. - Konfigurację CORS w API.
- Bramy API i proxy odwrotne.
- CDN-y i pamięci cache na krańcach sieci.
- Środowiska do autoryzacji i uwierzytelniania.
- Narzędzia do monitorowania, logowania i śledzenia.
- Routowanie oraz parsowanie treści w frameworku backendowym.
Część warstw może dopuszczać tylko ustaloną listę metod, a inne mogą odrzucać treść zapytań w przypadku nieznanych metod. Testuj rozwiązanie end-to-end w środowisku odpowiadającym warunkom produkcji, a nie tylko na serwerze lokalnym.
CORS zawsze wymaga procedury preflight
QUERY nie należy do metod z listy bezpiecznych dla CORS (GET, HEAD i POST). Oznacza to, że żądanie QUERY z innej domeny przez przeglądarkę zawsze wywołuje żądanie wstępne: przed rzeczywistym żądaniem przeglądarka wysyła żądanie OPTIONS, aby sprawdzić, czy QUERY jest dozwolone.
Jeśli twoja aplikacja React i API znajdują się na różnych domenach, serwer musi odpowiedzieć na to żądanie wstępne za pomocą nagłówka Access-Control-Allow-Methods, który zawiera QUERY wraz z wszystkimi innymi nagłówkami używanymi w twoim żądaniu, takimi jak Content-Type. Ciało żądania w formacie JSON również zmusza do wysłania żądania wstępnego dla POST, więc dodatkowy koszt jest często niewielki, ale metoda musi być wyraźnie wymieniona.
To łatwo przeoczyć podczas rozwoju. Narzędzia takie jak Postman i curl nie egzekwują zasad CORS, więc punkt końcowy może działać perfekcyjnie w tych narzędziach, a zawieść w przeglądarce.
Kasowanie z pamięci podręcznej jest możliwe, ale bardziej skomplikowane
Odpowiedzi na zapytania QUERY można przechowywać w pamięci cache, co stanowi jedną z głównych zalet tej metody w porównaniu z POST. Problem polega na tym, że odpowiedź zależy od treści ciała żądania, a nie tylko od adresu URL. Rozważmy te dwa ciała żądania wysłane do tego samego endpointu:
{
"brand": "Apple"
}
{
"brand": "Dell"
}
Muszą one zwracać różne wyniki. Dlatego pamięć cache musi uwzględnić treść żądania, a zazwyczaj także jego typ mediów, w kluczu cache przy decydowaniu o tym, czy przechowywaną odpowiedź można ponownie wykorzystać. Pamięci cache mogą również normalizować treść (na przykład ignorując nieważne spacje w plikach JSON), aby równoważne zapytania dzieliły ten sam wpis.
Dla deweloperów React i Next.js lekcją jest to, by przy przechowywaniu w cache nie traktować QUERY jako „GET z ciałem żądania”. Pamięci cache przeglądarki, CDN oraz pamięci fetch na poziomie frameworków mogą jeszcze nie używać kluczy opartych na treści ciała żądania, więc należy sprawdzić zachowanie każdej warstwy zamiast zakładać udane dostępy do cache.
Jak QUERY wiąże się z GraphQL
GraphQL często pojawia się w tych samych rozmowach, ale oba działają na różnych poziomach. GraphQL dostarcza języka do opisywania danych, których chcemy, oraz modelu do ich przetwarzania. QUERY to jedynie czynność na poziomie transmisji: określa, w jaki sposób żądanie zawierające treść zapytania powinno być traktowane przez HTTP. Dziś klienty GraphQL najczęściej wysyłają operacje za pomocą POST; serwer GraphQL w zasadzie mógłby akceptować operacje odczytu przez QUERY, ale QUERY nie zastępuje GraphQL, a oba mogą współistnieć w zależności od projektu API.
Czy powinieneś migrować istniejące API?
Zazwyczaj nie, przynajmniej nie tylko dlatego, że istnieje QUERY. Jeśli punkt końcowy do wyszukiwania już dobrze funkcjonuje przy użyciu GET lub POST, jego zmiana wiąże się z dodatkowymi pracami migracyjnymi i dotyczącymi kompatybilności przy ograniczonych korzyściach. QUERY ma największe znaczenie podczas projektowania nowego punktu końcowego, którego żądania są:
- Tylko do odczytu.
Daje zespołom racjonalną alternatywę dla używania POST, gdy żądanie GET staje się zbyt skomplikowane.
Główne wnioski
- Używaj GET do prostego pobierania danych i zachowuj prostość prostych żądań.
- Rozważ użycie QUERY dla złożonych, tylko do odczytu żądań, które wymagają strukturyzowanej treści.
- Nie oczekuj przyspieszenia: QUERY zmienia sposób reprezentacji żądania, a nie szybkość działania backendu lub sieci.
- Zanim zaczniesz używać tego rozwiązania w produkcji, sprawdź jego obsługę w przeglądarkach, proxy’ach, bramkach, CDN oraz w twoim frameworku backendowym.
- Konfiguruj CORS, aby wyraźnie zezwolić na ten metodę, i przetestuj to w rzeczywistej przeglądarce.
- Zanim polegniesz na zapisanych odpowiedziach QUERY, upewnij się, że wszystkie klucze warstw cache w ciele żądania zostały sprawdzone.
- Pilnuj przyjęcia tego rozwiązania w ekosystemie: strony wyszukiwawcze, panele sterowania oraz aplikacje wymagające dużej ilości danych to obszary, gdzie QUERY najprawdopodobniej przyniesie pierwsze korzyści.