Pular para o conteúdo

    Capítulo 4, Básico

    Parâmetros de caminho

    Uma rota que muda conforme um pedaço da URL: o produto 12, o usuário 7. Um parâmetro de caminho evita criar uma rota para cada valor.

    Um pedaço dinâmico da URL

    Entre chaves, o trecho vira uma variável que chega à função com o mesmo nome. Sem tipo, ele chega como texto. Com tipo, o FastAPI converte e valida:

    basico/cap04_parametros_caminho.pylinhas 10 a 22
    from fastapi import FastAPI
    from fastapi.testclient import TestClient
    
    app = FastAPI()
    
    
    @app.get("/usuarios/{usuario_id}")
    def obter_usuario(usuario_id: int):
        return {"usuario_id": usuario_id, "tipo": type(usuario_id).__name__}
    
    
    cliente = TestClient(app)
    print(cliente.get("/usuarios/200").json())
    
    Saída
    {'usuario_id': 200, 'tipo': 'int'}
    

    O "200" da URL chegou à função como o inteiro 200. Quando o valor não é do tipo pedido, a requisição é recusada antes de a função rodar, com um 422 que diz o que deu errado:

    basico/cap04_parametros_caminho.pylinhas 24 a 28
    resposta = cliente.get("/usuarios/abc")
    erro = resposta.json()["detail"][0]
    print(resposta.status_code)
    print(erro["loc"], erro["type"])
    print(erro["msg"])
    
    Saída
    422
    ['path', 'usuario_id'] int_parsing
    Input should be a valid integer, unable to parse string as an integer
    

    O campo loc diz onde está o problema (o caminho, o parâmetro usuario_id), o type classifica o erro, e o msg explica. Nenhuma linha do seu código foi escrita para isso.

    Restrições com `Path`

    Para impor regras além do tipo (maior que zero, no máximo tantos), use Path junto com Annotated. As restrições também aparecem na documentação:

    basico/cap04_parametros_caminho.pylinhas 33 a 46
    from typing import Annotated
    
    from fastapi import Path
    
    
    @app.get("/produtos/{produto_id}")
    def obter_produto(
        produto_id: Annotated[int, Path(gt=0, description="Identificador do produto")],
    ):
        return {"produto_id": produto_id}
    
    
    print(cliente.get("/produtos/5").status_code, cliente.get("/produtos/0").status_code)
    print(cliente.get("/produtos/0").json()["detail"][0]["msg"])
    
    Saída
    200 422
    Input should be greater than 0
    

    A ordem das rotas importa

    O FastAPI avalia as rotas na ordem em que foram declaradas, e a primeira que casa vence. Uma rota fixa como /usuarios/eu precisa vir antes da dinâmica /usuarios/{usuario_id}, senão o texto eu é tomado como o valor do parâmetro:

    basico/cap04_parametros_caminho.pylinhas 51 a 79
    app_errado = FastAPI()
    
    
    @app_errado.get("/pessoas/{pessoa_id}")
    def pessoa(pessoa_id: int):
        return {"id": pessoa_id}
    
    
    @app_errado.get("/pessoas/eu")
    def eu_errado():
        return {"quem": "eu"}
    
    
    print("dinâmica primeiro:", TestClient(app_errado).get("/pessoas/eu").status_code)
    
    app_certo = FastAPI()
    
    
    @app_certo.get("/pessoas/eu")
    def eu_certo():
        return {"quem": "eu"}
    
    
    @app_certo.get("/pessoas/{pessoa_id}")
    def pessoa_certa(pessoa_id: int):
        return {"id": pessoa_id}
    
    
    print("fixa primeiro:", TestClient(app_certo).get("/pessoas/eu").json())
    
    Saída
    dinâmica primeiro: 422
    fixa primeiro: {'quem': 'eu'}
    

    Valores de uma lista fechada

    Quando só alguns valores são aceitos, use um Enum. A validação e a documentação passam a listar as opções:

    basico/cap04_parametros_caminho.pylinhas 84 a 99
    from enum import Enum
    
    
    class Categoria(str, Enum):
        livros = "livros"
        jogos = "jogos"
    
    
    @app.get("/catalogo/{categoria}")
    def catalogo(categoria: Categoria):
        return {"categoria": categoria.value}
    
    
    print(cliente.get("/catalogo/jogos").json())
    resposta = cliente.get("/catalogo/roupas")
    print(resposta.status_code, resposta.json()["detail"][0]["type"])
    
    Saída
    {'categoria': 'jogos'}
    422 enum
    

    Caminhos com barras

    Um parâmetro de caminho normal não aceita /. Quando o valor é um caminho de arquivo, o conversor :path captura o resto inteiro:

    basico/cap04_parametros_caminho.pylinhas 104 a 109
    @app.get("/arquivos/{caminho:path}")
    def arquivo(caminho: str):
        return {"caminho": caminho}
    
    
    print(cliente.get("/arquivos/docs/2026/relatorio.pdf").json())
    
    Saída
    {'caminho': 'docs/2026/relatorio.pdf'}
    

    Parâmetro de caminho identifica um recurso

    Eu uso o caminho para identificar algo (o usuário 7, o pedido 123), e a query string (próximo capítulo) para filtrar ou ajustar (?ativo=true). Se a URL sem aquele valor não faria sentido, o valor pertence ao caminho.

    Exercício 1

    Um produto, validado

    Crie GET /itens/{item_id} com item_id inteiro maior que zero. Confira que 5 devolve 200, que 0 e abc devolvem 422.