Skip to content

    Chapter 5, Basic

    Query parameters

    What comes after the `?` in the URL: filters, search, sorting, pagination. They adjust **how** you want to see the data, and not **which** data is the resource.

    Required and optional parameters

    Any function parameter that does not form part of the path becomes a query parameter. Without a default value, it is required. With a default value, it is optional:

    basico/cap05_parametros_consulta.pylinhas 10 a 23
    from fastapi import FastAPI
    from fastapi.testclient import TestClient
    
    app = FastAPI()
    
    
    @app.get("/busca")
    def buscar(q: str, limite: int = 10, ordem: str | None = None):
        return {"q": q, "limite": limite, "ordem": ordem}
    
    
    cliente = TestClient(app)
    print(cliente.get("/busca?q=caneta").json())
    print(cliente.get("/busca?q=caneta&limite=50&ordem=preco").json())
    
    Output
    {'q': 'caneta', 'limite': 10, 'ordem': None}
    {'q': 'caneta', 'limite': 50, 'ordem': 'preco'}
    

    The limite assumed the default 10 when none was provided. The ordem stayed None (JSON null). The q has no standard, and forgetting it is a mistake:

    basico/cap05_parametros_consulta.pylinhas 25 a 27
    resposta = cliente.get("/busca")
    erro = resposta.json()["detail"][0]
    print(resposta.status_code, erro["loc"], erro["type"])
    
    Output
    422 ['query', 'q'] missing
    

    Optional: `str | None = None`, not `str = None`

    A lot of material writes nome: str = None. It works, but ly about the type: the function says it receives a str and returns None by default. The type checker complains, and the OpenAPI document loses the information that the value can be null:

    basico/cap05_parametros_consulta.pylinhas 32 a 44
    @app.get("/a")
    def com_none_simples(nome: str = None):
        return nome
    
    
    @app.get("/b")
    def com_uniao(nome: str | None = None):
        return nome
    
    
    caminhos = app.openapi()["paths"]
    print(caminhos["/a"]["get"]["parameters"][0]["schema"])
    print(caminhos["/b"]["get"]["parameters"][0]["schema"])
    
    Output
    {'type': 'string', 'title': 'Nome'}
    {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Nome'}
    

    The second form describes the truth (string or null), and it is the one I always use.

    Booleans and lists

    A bool accepts true, false, 1, 0, yes, no, on and off. To receive multiple values for the same parameter, declare a list with Query:

    basico/cap05_parametros_consulta.pylinhas 49 a 60
    from typing import Annotated
    
    from fastapi import Query
    
    
    @app.get("/produtos")
    def produtos(ativo: bool = True, etiquetas: Annotated[list[str] | None, Query()] = None):
        return {"ativo": ativo, "etiquetas": etiquetas}
    
    
    print(cliente.get("/produtos?ativo=no").json())
    print(cliente.get("/produtos?etiquetas=novo&etiquetas=oferta").json())
    
    Output
    {'ativo': False, 'etiquetas': None}
    {'ativo': True, 'etiquetas': ['novo', 'oferta']}
    

    Validation with `Query`

    The Query imposes minimum and maximum size on texts and limits on numbers, and the rules go to the documentation:

    basico/cap05_parametros_consulta.pylinhas 65 a 76
    @app.get("/pesquisa")
    def pesquisa(
        termo: Annotated[str, Query(min_length=3, max_length=20)],
        pagina: Annotated[int, Query(ge=1)] = 1,
        por_pagina: Annotated[int, Query(ge=1, le=100)] = 20,
    ):
        return {"termo": termo, "pagina": pagina, "por_pagina": por_pagina}
    
    
    print(cliente.get("/pesquisa?termo=caneta").json())
    resposta = cliente.get("/pesquisa?termo=ab&por_pagina=500")
    print(resposta.status_code, [e["loc"][-1] for e in resposta.json()["detail"]])
    
    Output
    {'termo': 'caneta', 'pagina': 1, 'por_pagina': 20}
    422 ['termo', 'por_pagina']
    

    The two errors came back together: the termo too short and the por_pagina above 100. FastAPI validates everything and informs everything at once, instead of stopping at the first problem.

    ParameterGoes inRequired?
    /usuarios/{id}PathAlways
    ?q=abc without defaultQueryYes
    ?q=abc with default or NoneQueryNo
    Pydantic ObjectBody (next chapter)According to the model

    Exercise 1

    A search with limits

    Create GET /livros with the parameters autor (optional), ano_minimo (integer, default 1900, minimum 1000) and limite (integer, default 10, between 1 and 50). Check the patterns and an invalid value.