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:
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())
{'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:
resposta = cliente.get("/usuarios/abc")
erro = resposta.json()["detail"][0]
print(resposta.status_code)
print(erro["loc"], erro["type"])
print(erro["msg"])
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:
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"])
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:
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())
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:
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"])
{'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:
@app.get("/arquivos/{caminho:path}")
def arquivo(caminho: str):
return {"caminho": caminho}
print(cliente.get("/arquivos/docs/2026/relatorio.pdf").json())
{'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.