From 1d327c7f55d96187eb45928bd4bd6de45818c2a3 Mon Sep 17 00:00:00 2001 From: Rauan Date: Wed, 26 Nov 2025 10:15:28 +0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docs=20for=20HTTP=20QUERY?= =?UTF-8?q?=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add docs source example in `docs_src/http_query/tutorial001.py` - Add documentation page `docs/en/docs/tutorial/http-query.md` - Add test for docs in `tests/test_tutorial/test_http_query/test_tutorial001.py` - Update `mkdocs.yml` navigation --- docs/en/docs/tutorial/http-query.md | 30 +++++++++++++++++ docs/en/mkdocs.yml | 1 + docs_src/http_query/tutorial001.py | 17 ++++++++++ .../test_http_query/test_tutorial001.py | 32 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 docs/en/docs/tutorial/http-query.md create mode 100644 docs_src/http_query/tutorial001.py create mode 100644 tests/test_tutorial/test_http_query/test_tutorial001.py diff --git a/docs/en/docs/tutorial/http-query.md b/docs/en/docs/tutorial/http-query.md new file mode 100644 index 000000000..a2ed698a7 --- /dev/null +++ b/docs/en/docs/tutorial/http-query.md @@ -0,0 +1,30 @@ +# HTTP QUERY + +Typically, when you want to read data you use `GET`. If you need to send complex data (like a large JSON object) to filter that data, you traditionally had to use `POST` because `GET` requests does not support request bodies. + +However, using `POST` for read-only operations isn't semantically correct, as `POST` implies that you are creating or modifying data. + +There is a newer HTTP method called **QUERY**. It is designed exactly for this: performing safe, idempotent read operations that require a request body. + +## Using `QUERY` + +In **FastAPI**, you can use the `QUERY` method using the `@app.query()` decorator. + +It works similarly to `@app.post()`, allowing you to receive Pydantic models in the body, but it signals that the operation is a read-only query. + + +{* ../../docs_src/http_query/tutorial001.py hl[7] *} + +### Testing it + +You can test it using an HTTP client that supports the `QUERY` method. + +Because it allows a body, you can send complex filters without hitting URL length limits common with `GET` query parameters. + +### Technical Details + +The `QUERY` method is defined in the [IETF HTTP QUERY Method Draft](https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html). It is considered: + +* **Safe**: It does not alter the state of the server (read-only). +* **Idempotent**: Making the same request multiple times yields the same result. + diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index fd346a3d3..b549d04bd 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -136,6 +136,7 @@ nav: - tutorial/request-forms-and-files.md - tutorial/handling-errors.md - tutorial/path-operation-configuration.md + - tutorial/http-query.md - tutorial/encoder.md - tutorial/body-updates.md - Dependencies: diff --git a/docs_src/http_query/tutorial001.py b/docs_src/http_query/tutorial001.py new file mode 100644 index 000000000..2fd51acd0 --- /dev/null +++ b/docs_src/http_query/tutorial001.py @@ -0,0 +1,17 @@ +from typing import Optional + +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class ItemSearch(BaseModel): + keyword: str + min_price: Optional[float] = None + max_price: Optional[float] = None + + +@app.query("/items/") +async def search_items(search_params: ItemSearch): + return {"message": "Searching items", "search_params": search_params} \ No newline at end of file diff --git a/tests/test_tutorial/test_http_query/test_tutorial001.py b/tests/test_tutorial/test_http_query/test_tutorial001.py new file mode 100644 index 000000000..5a29d2f0f --- /dev/null +++ b/tests/test_tutorial/test_http_query/test_tutorial001.py @@ -0,0 +1,32 @@ +from fastapi.testclient import TestClient + +from docs_src.http_query.tutorial001 import app + +client = TestClient(app) + + +def test_query_items(): + response = client.request( + "QUERY", + "/items/", + json={"keyword": "book", "max_price": 50}, + ) + assert response.status_code == 200 + assert response.json() == { + "message": "Searching items", + "search_params": { + "keyword": "book", + "min_price": None, + "max_price": 50.0, + }, + } + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200 + schema = response.json() + assert "query" in schema["paths"]["/items/"] + operation = schema["paths"]["/items/"]["query"] + assert "requestBody" in operation + assert "ItemSearch" in operation["requestBody"]["content"]["application/json"]["schema"]["$ref"]