Python Framework

FastAPI

FastAPI é o framework async de Python construído sobre type hints. O Pydantic valida seus dados, o Starlette gerencia o HTTP e a documentação OpenAPI é gerada automaticamente.

intermediate15 min readUpdated 16 de set. de 2026
main.py
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.post("/items", status_code=201)
async def create_item(item: Item) -> Item:
    return item
Lançado
2018
Linguagem
Python
Construído sobre
Starlette + Pydantic
Ideia central
Type hints como contrato
Docs
OpenAPI e Swagger UI
Versão atual
0.11x

Por que importa

Por que o FastAPI parece mágica (mas não é)

Tipos são a API

Declare um parâmetro ou um modelo com type hints e o FastAPI valida as requisições, serializa as respostas e documenta o endpoint a partir da mesma declaração.

Async quando você precisar

Construído sobre o toolkit ASGI do Starlette, permitindo que endpoints async lidem com I/O concorrente sem bloquear o event loop.

Documentação gratuita

Cada endpoint aparece no Swagger UI e ReDoc interativos, gerados a partir do esquema OpenAPI que o FastAPI constrói na inicialização.

O panorama completo

Tipos, Pydantic e dependências

Type hints descrevem o contrato, o Pydantic o impõe e as dependências injetam as peças compartilhadas que uma requisição precisa.

Type hints

Descrever

Anotar parâmetros informa ao FastAPI de onde vem um valor e qual formato ele possui, fazendo com que a validação ocorra antes da execução da sua função.

Modelos Pydantic

Validar

Classes BaseModel analisam, convertem e rejeitam dados, servindo também como o contrato de resposta e o esquema gerado.

Dependências

Injetar

O Depends() compõe lógicas compartilhadas, como sessões, autenticação e configurações, resolvendo o grafo por requisição e reutilizando resultados em cache.

HTML5 de uma olhada

A caixa de ferramentas do FastAPI

Operações de rota

@app.get e @app.post vinculam uma URL a uma função, com parâmetros de rota e query tipados.

Modelos Pydantic

Classes BaseModel validam corpos de requisição e moldam as respostas.

Documentação automática

Swagger UI em /docs e ReDoc em /redoc, gerados a partir do seu código.

Dependências

Depends() injeta sessões, usuários, paginação e configurações.

Suporte Async

Endpoints async def rodam em ASGI e aguardam I/O concorrentemente.

Tarefas em segundo plano

Execute trabalhos após a resposta ser enviada sem a necessidade de adicionar uma fila.

O guia completo

FastAPI: Tudo que voce precisa saber

O que é FastAPI?

FastAPI é um framework web moderno em Python para a construção de APIs. Sua ideia central é que seus type hints são o contrato. Declare um parâmetro como int, um corpo como um modelo Pydantic, ou um tipo de retorno como ItemOut, e o framework valida a requisição, serializa a resposta e escreve a documentação — tudo a partir dessa única declaração.

Ele é construído sobre duas bibliotecas que você deve conhecer. Starlette fornece o toolkit ASGI: roteamento, middleware e o ciclo de requisição/resposta. Pydantic fornece a validação e a serialização. FastAPI é a camada que os combina, adiciona injeção de dependência e gera um esquema OpenAPI na inicialização.

O resultado é que uma função pequena e legível entrega a você um endpoint validado com documentação interativa. A mágica é real, mas é mecânica, e entender essa mecânica é o objetivo deste guia.

Operações de caminho

Uma operação de caminho (path operation) é uma função vinculada a um método HTTP e a uma URL. O decorador nomeia o método, e a assinatura da função informa ao FastAPI o que esperar.

# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello, world"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

O segmento {item_id} é um parâmetro de caminho, e sua anotação int significa que o FastAPI converte a string e retorna automaticamente um erro 422 caso não consiga. Qualquer coisa que não esteja no caminho é tratada como um parâmetro de query, e um valor padrão de None o torna opcional. Não há parsing manual nem request.args.

Agrupe operações relacionadas com um APIRouter, que pode conter um prefixo e tags para a documentação.

from fastapi import APIRouter

router = APIRouter(prefix="/items", tags=["items"])


@router.get("")
async def list_items():
    return []


@router.post("", status_code=201)
async def create_item():
    return {}

Depois, inclua o router no app: app.include_router(router). É assim que um projeto FastAPI se mantém modular.

Modelos Pydantic

Um modelo Pydantic é uma classe que herda de BaseModel. Ele analisa a entrada, converte tipos compatíveis, aplica restrições e gera um erro de validação claro quando algo está errado.

# app/schemas.py
from datetime import datetime

from pydantic import BaseModel, Field


class ItemBase(BaseModel):
    name: str = Field(min_length=1, max_length=120)
    price: float = Field(gt=0)
    tags: list[str] = []


class ItemCreate(ItemBase):
    pass


class ItemOut(ItemBase):
    id: int
    created_at: datetime

    model_config = {"from_attributes": True}

Field adiciona restrições e metadados. model_config = {"from_attributes": True} permite que o Pydantic leia atributos de um objeto ORM, para que você possa retornar uma linha do banco de dados diretamente e tê-la serializada. A separação entre ItemCreate e ItemOut é deliberada: o modelo de entrada não possui id, e o modelo de saída pode expor campos que o cliente não pode definir.

Use um modelo Pydantic como o tipo do corpo (body) e o FastAPI analisa, valida e entrega a você um objeto tipado.

@app.post("/items", response_model=ItemOut, status_code=201)
async def create_item(item: ItemCreate):
    return await repo.create(item)

Se o corpo for inválido, o FastAPI retorna um erro 422 com uma lista dos campos exatos que falharam. Você não precisou escrever sequer uma linha de validação.

Corpos de requisição e validação

O FastAPI distingue as fontes de dados por posição e tipo:

  • Parâmetros de path vêm da URL.
  • Parâmetros de query são todo o restante na assinatura.
  • Um modelo Pydantic é o corpo da requisição (request body).
  • Header, Cookie, Form e File marcam essas entradas especiais.
from fastapi import Header, Query


@app.get("/search")
async def search(
    q: str = Query(min_length=1, max_length=50),
    limit: int = Query(default=20, ge=1, le=100),
    user_agent: str | None = Header(default=None),
):
    return {"q": q, "limit": limit, "user_agent": user_agent}

As restrições nos parâmetros de query são aplicadas da mesma forma que nos modelos, e a documentação gerada exibe esses limites. Essa consistência é a razão pela qual o código do FastAPI é tão compacto.

Documentação automática

Como o schema é gerado a partir do código, a documentação nunca fica desatualizada. Inicie o app e abra /docs para o Swagger UI, onde você pode chamar os endpoints diretamente, ou /redoc para uma referência mais limpa.

app = FastAPI(
    title="Inventory API",
    version="1.0.0",
    description="Items, stock levels and orders.",
)

Você pode adicionar resumos, descrições e exemplos às operações de path e aos campos, e eles aparecerão na documentação. Para equipes de frontend, o mesmo schema pode gerar clientes tipados, mantendo a API e seus consumidores em sincronia. Esse é o benefício de tratar os tipos como o contrato.

Dependências

Uma dependência é uma função que o FastAPI chama antes do seu endpoint e cujo valor de retorno é injetado. Depends() é o mecanismo, e ele é composto.

from fastapi import Depends, HTTPException


async def get_session() -> AsyncSession:
    async with SessionLocal() as session:
        yield session


async def get_current_user(
    token: str = Depends(get_token),
    session: AsyncSession = Depends(get_session),
) -> User:
    user = await authenticate(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Not authenticated")
    return user
@app.get("/me", response_model=UserOut)
async def read_me(user: User = Depends(get_current_user)):
    return user

As dependências podem ser síncronas ou assíncronas, podem usar yield para executar a limpeza após a resposta e são armazenadas em cache dentro de uma requisição, portanto get_session é chamado apenas uma vez, mesmo que várias dependências precisem dele. Elas são o local idiomático para sessões de banco de dados, autenticação, paginação e feature flags.

Async versus sync

O FastAPI suporta endpoints def e async def, e essa escolha é importante.

  • Um endpoint async def é executado no event loop. Toda chamada de I/O dentro dele deve ser aguardada (awaited) por meio de uma biblioteca async, caso contrário, ele bloqueará o loop para todas as outras requisições.
  • Um endpoint def comum é executado em um thread pool, portanto, chamadas bloqueantes são seguras, mas cada requisição ocupa uma thread.
# Async: use an async driver and await it
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()


# Sync: blocking code is fine; FastAPI offloads it
@app.get("/report")
def build_report():
    return generate_report()  # CPU or blocking I/O

A regra é simples: async do início ao fim, ou sync do início ao fim. Misturar uma chamada bloqueante em um endpoint async def é o bug de performance mais comum no FastAPI, e ele permanece invisível até que o tráfego aumente.

Erros e códigos de status

Lance HTTPException para retornar um erro com um código de status e detalhes em JSON. O FastAPI o transforma em uma resposta e documenta o código de status.

from fastapi import HTTPException, status


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await repo.get(item_id)
    if item is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Item not found",
        )
    return item

Para manter um formato de erro consistente em toda a API, registre exception handlers que convertam erros do framework e do domínio em um único envelope. Mantenha o campo detail estável; os clientes irão parseá-lo.

Tarefas em segundo plano (Background tasks)

BackgroundTasks executa tarefas após a resposta ser enviada, no mesmo processo. É a opção leve para enviar um e-mail ou gravar uma linha de auditoria.

from fastapi import BackgroundTasks


def send_receipt(email: str) -> None:
    ...


@app.post("/orders", status_code=201)
async def create_order(
    order: OrderCreate,
    tasks: BackgroundTasks,
):
    saved = await repo.create(order)
    tasks.add_task(send_receipt, saved.email)
    return saved

Tarefas em segundo plano não são uma fila de jobs. Elas rodam no mesmo processo, portanto, são perdidas se o worker reiniciar e competem com o processamento de requisições. Para qualquer coisa que exija durabilidade, utilize Celery, RQ ou uma fila gerenciada.

Bancos de Dados

O FastAPI não possui uma camada de banco de dados, então você deve escolher a sua. As escolhas mais comuns são SQLAlchemy com um engine async ou SQLModel, que combina SQLAlchemy com Pydantic. O padrão de dependência fornece a sessão e a fecha após a requisição.

# app/db.py
from collections.abc import AsyncIterator

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

engine = create_async_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)


async def get_session() -> AsyncIterator[AsyncSession]:
    async with SessionLocal() as session:
        yield session
@router.post("", response_model=ItemOut, status_code=201)
async def create_item(
    payload: ItemCreate,
    session: AsyncSession = Depends(get_session),
):
    item = Item(**payload.model_dump())
    session.add(item)
    await session.commit()
    await session.refresh(item)
    return item

Use expire_on_commit=False para que os objetos permaneçam utilizáveis após o commit e mantenha as transações dentro da requisição. Se preferir um driver síncrono, use endpoints def simples para que o FastAPI possa delegá-los ao thread pool.

Segurança

O FastAPI fornece os blocos fundamentais para OAuth2 e JWT sem impor um sistema de autenticação completo. OAuth2PasswordBearer extrai o token, e python-jose ou pyjwt o verifica.

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/users/me", response_model=UserOut)
async def read_me(
    token: str = Depends(oauth2_scheme),
    session: AsyncSession = Depends(get_session),
):
    user = await get_user_from_token(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

Armazene hashes de senhas com passlib ou argon2-cffi, nunca em texto simples. Faça o hash e a verificação fora do endpoint, mantenha os segredos nas configurações e adicione rate limiting e CORS de forma deliberada. O FastAPI oferece as ferramentas; a política ainda cabe a você definir.

Configuração e definições

Mantenha a configuração em um único objeto tipado em vez de ler variáveis de ambiente em todo o código. pydantic-settings valida o ambiente na inicialização e oferece a mesma segurança de tipos que seus modelos.

# app/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")

    database_url: str
    secret_key: str
    access_token_minutes: int = 30


settings = Settings()
from functools import lru_cache

from app.config import Settings


@lru_cache
def get_settings() -> Settings:
    return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
    return {"token_minutes": settings.access_token_minutes}

Como as definições são injetadas como qualquer outra dependência, os testes podem sobrescrevê-las com app.dependency_overrides[get_settings]. A ausência de variáveis obrigatórias causa falha na inicialização, e não na primeira requisição, que é exatamente quando você deseja descobrir o problema.

Testes

TestClient envolve o app em um cliente similar ao WSGI e faz requisições reais no mesmo processo, incluindo validação e substituição de dependências (dependency overrides).

# tests/test_items.py
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)


def test_create_item():
    response = client.post("/items", json={"name": "Cup", "price": 9.5})
    assert response.status_code == 201
    assert response.json()["name"] == "Cup"


def test_invalid_price():
    response = client.post("/items", json={"name": "Cup", "price": -1})
    assert response.status_code == 422

Use app.dependency_overrides para trocar a sessão do banco de dados por uma de teste, e escreva testes assíncronos com httpx.AsyncClient quando precisar testar endpoints assíncronos diretamente. Como as dependências são explícitas, substituí-las nos testes é simples.

Melhores práticas

  • Declare explicitamente os modelos de request e response; nunca aceite ou retorne dicionários puros.
  • Mantenha os endpoints async def totalmente async, ou use def para que o FastAPI delegue o trabalho bloqueante.
  • Coloque a lógica compartilhada em dependências Depends() em vez de repeti-la em cada rota.
  • Divida o app com APIRouter e atribua a cada router um prefixo e tags.
  • Adicione restrições com Field e Query em vez de validar manualmente.
  • Lance HTTPException com o status code correto e um detail estável.
  • Sobrescreva dependências em testes em vez de fazer patch de componentes internos.
  • Mantenha segredos em objetos de configurações lidos do ambiente.

Erros comuns

  • Chamar código bloqueante dentro de um endpoint async def e travar o event loop.
  • Retornar objetos ORM sem um response_model, expondo colunas internas.
  • Esquecer o from_attributes = True e questionar por que a serialização falha.
  • Escrever um único main.py gigante em vez de utilizar routers e módulos.
  • Tratar BackgroundTasks como uma fila de jobs durável.
  • Compartilhar uma única sessão entre requisições ou entre tarefas assíncronas.
  • Validar a mesma entrada duas vezes porque o modelo Pydantic foi ignorado.
  • Capturar todas as exceções e retornar 200 com um corpo de erro.

Próximos passos

O FastAPI transforma type hints em uma API validada e documentada, e é por isso que ele se tornou o padrão para novos serviços em Python. Para comparar as vantagens e desvantagens, leia o guia do Flask para a abordagem síncrona minimalista e o do Django para um framework completo com ORM e admin. Depois, aprofunde-se no contrato de API com OpenAPI e nas regras de design de [REST APIs](/guides/rest], e certifique-se de que seus endpoints valham a pena ser documentados definindo corretamente o modelo de recurso.

Na pratica

Um endpoint, quatro camadas

O modelo declara o formato, a operação de rota o expõe, as dependências fornecem o contexto e a chamada ao banco de dados realiza o trabalho.

app/main.py
from fastapi import FastAPI, HTTPException

from app.schemas import ItemOut

app = FastAPI(title="Inventory API")


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await db.items.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item

Async e I/O bloqueante

Um endpoint async roda no event loop, portanto, uma chamada bloqueante trava todas as outras requisições. Use um driver async com async def, ou um endpoint def simples que o FastAPI executa em um thread pool.

Preferir
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()
Evitar
@app.get("/items")
async def list_items():
    # blocks the event loop for the whole query
    return session.query(Item).all()

Moldando a resposta

Um response_model filtra a saída para os campos declarados e a documenta, garantindo que colunas internas nunca vazem para os clientes.

Preferir
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
    return await repo.get(user_id)
Evitar
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # returns password_hash and every other column
    return await repo.get(user_id)

Trade-offs

O FastAPI é a escolha padrão correta?

O FastAPI é otimizado para APIs tipadas e throughput async. Isso é excelente para serviços, mas inadequado quando a aplicação consiste majoritariamente em páginas renderizadas no servidor.

Strengths

  • Menos código, menos bugs

    Validação, serialização e documentação vêm dos mesmos type hints, eliminando a necessidade de um segundo esquema para manter a sincronia.

  • Documentação automática

    Um esquema OpenAPI vivo e docs interativos tornam a API explorável para equipes de frontend e clientes desde o primeiro dia.

  • Construído para o Python moderno

    Endpoints async, injeção de dependência e Pydantic v2 o tornam rápido e agradável sem a necessidade de um framework pesado.

Trade-offs

  • Async tem armadilhas

    Uma única chamada bloqueante dentro de um endpoint async pode travar todo o worker. Você precisa saber quais bibliotecas são verdadeiramente async.

  • É um framework de API

    O FastAPI não fornece templates, painel administrativo ou ORM. Apps renderizados no servidor precisam de outra camada ou de um framework diferente.

  • O ecossistema é mais jovem

    As versões do Pydantic e SQLAlchemy evoluem rapidamente e os padrões mudam. Espere ter que manter as dependências atualizadas.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender FastAPI?

Nosso tutorial interativo te guia por FastAPI passo a passo — com quizzes e codigo real que voce pode executar no navegador.