Ir para o conteúdo

Corpo da requisição

Quando você precisa enviar dados de um cliente (como de um navegador) para sua API, você os envia como um corpo da requisição.

O corpo da requisição é a informação enviada pelo cliente para sua API. O corpo da resposta é a informação que sua API envia para o cliente.

Sua API quase sempre precisa enviar um corpo na resposta. Mas os clientes não necessariamente precisam enviar corpos de requisição o tempo todo, às vezes eles apenas requisitam um path, talvez com alguns parâmetros de consulta, mas não enviam um corpo.

Para declarar um corpo da requisição, você utiliza os modelos do Pydantic com todos os seus poderes e benefícios.

Informação

Para enviar dados, você deve usar um dos: POST (o mais comum), PUT, DELETE ou PATCH.

Enviar um corpo em uma requisição GET não tem um comportamento definido nas especificações, porém é suportado pelo FastAPI, apenas para casos de uso bem complexos/extremos.

Como é desencorajado, a documentação interativa com Swagger UI não irá mostrar a documentação para o corpo da requisição para um GET, e proxies que intermediarem podem não suportar o corpo da requisição.

Importe o BaseModel do Pydantic

Primeiro, você precisa importar BaseModel do pydantic:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

Crie seu modelo de dados

Então você declara seu modelo de dados como uma classe que herda BaseModel.

Utilize os tipos Python padrão para todos os atributos:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

Assim como quando declaramos parâmetros de consulta, quando um atributo do modelo possui um valor padrão, ele se torna opcional. Caso contrário, se torna obrigatório. Use None para torná-lo opcional.

Por exemplo, o modelo acima declara um JSON "object" (ou dict no Python) como esse:

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

...como description e tax são opcionais (com um valor padrão de None), esse JSON "object" também é válido:

{
    "name": "Foo",
    "price": 45.2
}

Declare como um parâmetro

Para adicioná-lo à sua operação de rota, declare-o da mesma maneira que você declarou parâmetros de rota e de consulta:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

...e declare o seu tipo como o modelo que você criou, Item.

Resultados

Apenas com essa declaração de tipos do Python, o FastAPI irá:

  • Ler o corpo da requisição como um JSON.
  • Converter os tipos correspondentes (se necessário).
  • Validar os dados.
    • Se algum dado for inválido, irá retornar um erro bem claro, indicando exatamente onde e o que estava incorreto.
  • Entregar a você a informação recebida no parâmetro item.
    • Como você o declarou na função como do tipo Item, você também terá o suporte do editor (completação, etc) para todos os atributos e seus tipos.
  • Gerar definições de JSON Schema para o seu modelo; você também pode usá-las em qualquer outro lugar se fizer sentido para o seu projeto.
  • Esses schemas farão parte do esquema OpenAPI gerado, e serão usados pelas UIs de documentação automática.

Documentação automática

Os JSON Schemas dos seus modelos farão parte do esquema OpenAPI gerado para sua aplicação, e aparecerão na documentação interativa da API:

E também serão utilizados na documentação da API dentro de cada operação de rota que precisar deles:

Suporte do editor

No seu editor, dentro da função você receberá dicas de tipos e completação em todo lugar (isso não aconteceria se você recebesse um dict em vez de um modelo Pydantic):

Você também poderá receber verificações de erros para operações de tipos incorretas:

Isso não é por acaso, todo o framework foi construído em volta deste design.

E foi imensamente testado na fase de design, antes de qualquer implementação, para garantir que funcionaria para todos os editores de texto.

Houveram mudanças no próprio Pydantic para que isso fosse possível.

As capturas de tela anteriores foram capturas no Visual Studio Code.

Mas você terá o mesmo suporte do editor no PyCharm e na maioria dos editores Python:

Dica

Se você utiliza o PyCharm como editor, você pode utilizar o Plugin do Pydantic para o PyCharm .

Melhora o suporte do editor para seus modelos Pydantic com:

  • preenchimento automático
  • verificação de tipos
  • refatoração
  • buscas
  • inspeções

Use o modelo

Dentro da função, você pode acessar todos os atributos do objeto do modelo diretamente:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax is not None:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax is not None:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

Informação

No Pydantic v1 o método se chamava .dict(), ele foi descontinuado (mas ainda é suportado) no Pydantic v2, e renomeado para .model_dump().

Os exemplos aqui usam .dict() para compatibilidade com o Pydantic v1, mas você deve usar .model_dump() se puder usar o Pydantic v2.

Corpo da requisição + parâmetros de rota

Você pode declarar parâmetros de rota e corpo da requisição ao mesmo tempo.

O FastAPI irá reconhecer que os parâmetros da função que combinam com parâmetros de rota devem ser retirados da rota, e que parâmetros da função que são declarados como modelos Pydantic sejam retirados do corpo da requisição.

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

Corpo da requisição + parâmetros de rota + parâmetros de consulta

Você também pode declarar parâmetros de corpo, rota e consulta, ao mesmo tempo.

O FastAPI irá reconhecer cada um deles e retirar a informação do local correto.

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: Union[str, None] = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result

Os parâmetros da função serão reconhecidos conforme abaixo:

  • Se o parâmetro também é declarado no path, será utilizado como um parâmetro de rota.
  • Se o parâmetro é de um tipo único (como int, float, str, bool, etc) será interpretado como um parâmetro de consulta.
  • Se o parâmetro é declarado como um modelo Pydantic, será interpretado como o corpo da requisição.

Nota

O FastAPI saberá que o valor de q não é obrigatório por causa do valor padrão = None.

O str | None (Python 3.10+) ou o Union em Union[str, None] (Python 3.8+) não é utilizado pelo FastAPI para determinar que o valor não é obrigatório, ele saberá que não é obrigatório porque tem um valor padrão = None.

Mas adicionar as anotações de tipo permitirá ao seu editor oferecer um suporte melhor e detectar erros.

Sem o Pydantic

Se você não quer utilizar os modelos Pydantic, você também pode utilizar o parâmetro Body. Veja a documentação para Body - Parâmetros múltiplos: Valores singulares no body.