Browse Source
- 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` navigationpull/14408/head
4 changed files with 80 additions and 0 deletions
@ -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. |
||||
|
|
||||
@ -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} |
||||
@ -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"] |
||||
Loading…
Reference in new issue